From 5cd3ee6e994e29cc23fc3b857df1085c3c0a7d27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 21 Apr 2015 13:13:49 +0200 Subject: [PATCH 01/72] Port to LLVM 3.7 --- libevmjit/Array.cpp | 20 ++++++++++---------- libevmjit/Cache.cpp | 17 +++++++---------- libevmjit/Cache.h | 4 ++-- libevmjit/ExecutionEngine.cpp | 13 ++++++------- libevmjit/Optimizer.cpp | 4 ++-- libevmjit/RuntimeManager.cpp | 12 ++++++------ libevmjit/Type.h | 5 +++-- 7 files changed, 36 insertions(+), 39 deletions(-) diff --git a/libevmjit/Array.cpp b/libevmjit/Array.cpp index 3266038db..b3d3cc0bb 100644 --- a/libevmjit/Array.cpp +++ b/libevmjit/Array.cpp @@ -47,9 +47,9 @@ llvm::Function* Array::createArrayPushFunc() auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func); m_builder.SetInsertPoint(entryBB); - auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); - auto sizePtr = m_builder.CreateStructGEP(arrayPtr, 1, "sizePtr"); - auto capPtr = m_builder.CreateStructGEP(arrayPtr, 2, "capPtr"); + auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); + auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); + auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); auto data = m_builder.CreateLoad(dataPtr, "data"); auto size = m_builder.CreateLoad(sizePtr, "size"); auto cap = m_builder.CreateLoad(capPtr, "cap"); @@ -96,7 +96,7 @@ llvm::Function* Array::createArraySetFunc() InsertPointGuard guard{m_builder}; m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); - auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); + auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); auto data = m_builder.CreateLoad(dataPtr, "data"); auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); m_builder.CreateStore(value, valuePtr); @@ -118,7 +118,7 @@ llvm::Function* Array::createArrayGetFunc() InsertPointGuard guard{m_builder}; m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); - auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); + auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); auto data = m_builder.CreateLoad(dataPtr, "data"); auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); auto value = m_builder.CreateLoad(valuePtr, "value"); @@ -163,7 +163,7 @@ llvm::Function* Array::createFreeFunc() InsertPointGuard guard{m_builder}; m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); - auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); + auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr"); auto data = m_builder.CreateLoad(dataPtr, "data"); auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem"); m_builder.CreateCall(freeFunc, mem); @@ -199,8 +199,8 @@ llvm::Function* Array::createExtendFunc() InsertPointGuard guard{m_builder}; m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");// TODO: Use byte* in Array - auto sizePtr = m_builder.CreateStructGEP(arrayPtr, 1, "sizePtr"); - auto capPtr = m_builder.CreateStructGEP(arrayPtr, 2, "capPtr"); + auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr"); + auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr"); auto data = m_builder.CreateLoad(dataPtr, "data"); auto size = m_builder.CreateLoad(sizePtr, "size"); auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize"); @@ -246,7 +246,7 @@ Array::Array(llvm::IRBuilder<>& _builder, llvm::Value* _array) : void Array::pop(llvm::Value* _count) { - auto sizePtr = m_builder.CreateStructGEP(m_array, 1, "sizePtr"); + auto sizePtr = m_builder.CreateStructGEP(getType(), m_array, 1, "sizePtr"); auto size = m_builder.CreateLoad(sizePtr, "size"); auto newSize = m_builder.CreateNUWSub(size, _count, "newSize"); m_builder.CreateStore(newSize, sizePtr); @@ -254,7 +254,7 @@ void Array::pop(llvm::Value* _count) llvm::Value* Array::size(llvm::Value* _array) { - auto sizePtr = m_builder.CreateStructGEP(_array ? _array : m_array, 1, "sizePtr"); + auto sizePtr = m_builder.CreateStructGEP(getType(), _array ? _array : m_array, 1, "sizePtr"); return m_builder.CreateLoad(sizePtr, "array.size"); } diff --git a/libevmjit/Cache.cpp b/libevmjit/Cache.cpp index 47a6386e9..aa316d51d 100644 --- a/libevmjit/Cache.cpp +++ b/libevmjit/Cache.cpp @@ -24,7 +24,7 @@ namespace jit namespace { CacheMode g_mode; - llvm::MemoryBuffer* g_lastObject; + std::unique_ptr g_lastObject; ExecutionEngineListener* g_listener; static const size_t c_versionStampLength = 32; @@ -79,8 +79,7 @@ void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map Cache::getObject(std::string const& id) } -void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) +void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) { // Only in "on" and "write" mode if (g_mode != CacheMode::on && g_mode != CacheMode::write) @@ -154,17 +153,15 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory llvm::sys::path::append(cachePath, id); DLOG(cache) << id << ": write\n"; - std::string error; + std::error_code error; llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None); - cacheFile << _object->getBuffer() << getLibVersionStamp(); + cacheFile << _object.getBuffer() << getLibVersionStamp(); } -llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module) +std::unique_ptr ObjectCache::getObject(llvm::Module const* _module) { DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; - auto o = g_lastObject; - g_lastObject = nullptr; - return o; + return std::move(g_lastObject); } } diff --git a/libevmjit/Cache.h b/libevmjit/Cache.h index f6a0a3400..6b8457f4b 100644 --- a/libevmjit/Cache.h +++ b/libevmjit/Cache.h @@ -32,13 +32,13 @@ class ObjectCache : public llvm::ObjectCache { public: /// notifyObjectCompiled - Provides a pointer to compiled code for Module M. - virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) final override; + virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) final override; /// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that /// contains the object which corresponds with Module M, or 0 if an object is /// not available. The caller owns both the MemoryBuffer returned by this /// and the memory it references. - virtual llvm::MemoryBuffer* getObject(llvm::Module const* _module) final override; + virtual std::unique_ptr getObject(llvm::Module const* _module) final override; }; diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index e15dad969..9bd767158 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -131,20 +131,20 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) llvm::InitializeNativeTargetAsmPrinter(); auto module = std::unique_ptr(new llvm::Module({}, llvm::getGlobalContext())); - llvm::EngineBuilder builder(module.get()); - builder.setEngineKind(llvm::EngineKind::JIT); - builder.setUseMCJIT(true); - builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); + // FIXME: LLVM 3.7: test on Windows auto triple = llvm::Triple(llvm::sys::getProcessTriple()); if (triple.getOS() == llvm::Triple::OSType::Win32) triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format module->setTargetTriple(triple.str()); + llvm::EngineBuilder builder(std::move(module)); + builder.setEngineKind(llvm::EngineKind::JIT); + builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); + ee.reset(builder.create()); if (!CHECK(ee)) return ReturnCode::LLVMConfigError; - module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module ee->setObjectCache(objectCache); if (preloadCache) @@ -179,8 +179,7 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) if (g_dump) module->dump(); - ee->addModule(module.get()); - module.release(); + ee->addModule(std::move(module)); listener->stateChanged(ExecState::CodeGen); entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); } diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index 84a7c3a6a..731fda5c6 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -1,7 +1,7 @@ #include "Optimizer.h" #include "preprocessor/llvm_includes_start.h" -#include +#include #include #include #include "preprocessor/llvm_includes_end.h" @@ -15,7 +15,7 @@ namespace jit bool optimize(llvm::Module& _module) { - auto pm = llvm::PassManager{}; + auto pm = llvm::legacy::PassManager{}; //pm.add(llvm::createFunctionInliningPass(2, 2)); // Produces invalid IR pm.add(llvm::createCFGSimplificationPass()); //pm.add(llvm::createInstructionCombiningPass()); // Produces invalid runtime results diff --git a/libevmjit/RuntimeManager.cpp b/libevmjit/RuntimeManager.cpp index dc7bc24a3..5c66a5a21 100644 --- a/libevmjit/RuntimeManager.cpp +++ b/libevmjit/RuntimeManager.cpp @@ -93,13 +93,13 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB // Unpack data auto rtPtr = getRuntimePtr(); - m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); + m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data"); assert(m_dataPtr->getType() == Type::RuntimeDataPtr); - m_gasPtr = m_builder.CreateStructGEP(m_dataPtr, 0, "gas"); + m_gasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), m_dataPtr, 0, "gas"); assert(m_gasPtr->getType() == Type::Gas->getPointerTo()); - m_memPtr = m_builder.CreateStructGEP(rtPtr, 2, "mem"); + m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem"); assert(m_memPtr->getType() == Array::getType()->getPointerTo()); - m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env"); + m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env"); assert(m_envPtr->getType() == Type::EnvPtr); m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize"); @@ -160,7 +160,7 @@ llvm::Value* RuntimeManager::getDataPtr() return m_dataPtr; auto rtPtr = getRuntimePtr(); - auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); + auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data"); assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo()); return dataPtr; } @@ -173,7 +173,7 @@ llvm::Value* RuntimeManager::getEnvPtr() llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) { - auto ptr = getBuilder().CreateStructGEP(getDataPtr(), _index); + auto ptr = getBuilder().CreateStructGEP(getRuntimeDataType(), getDataPtr(), _index); assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType()); return ptr; } diff --git a/libevmjit/Type.h b/libevmjit/Type.h index ffacc5407..fcca98d74 100644 --- a/libevmjit/Type.h +++ b/libevmjit/Type.h @@ -2,8 +2,9 @@ #include "preprocessor/llvm_includes_start.h" #include -#include -#include "preprocessor/llvm_includes_end.h" +#include +#include +#include "preprocessor/llvm_includes_end.h" // FIXME: LLVM 3.7: check if needed #include "Common.h" From 2b9eab188a29a6235a96c98f64eacaf07a47b268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 21 Apr 2015 16:23:01 +0200 Subject: [PATCH 02/72] Remove some LLVM 3.5 bugs workarounds --- libevmjit/Arith256.cpp | 11 ---------- libevmjit/Compiler.cpp | 8 ++----- libevmjit/Endianness.cpp | 5 ++--- libevmjit/ExecutionEngine.cpp | 15 +------------ libevmjit/GasMeter.cpp | 22 ++++++-------------- libevmjit/Optimizer.cpp | 4 ++-- libevmjit/preprocessor/llvm_includes_end.h | 4 ---- libevmjit/preprocessor/llvm_includes_start.h | 8 ------- 8 files changed, 13 insertions(+), 64 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index e88fda432..029eb9370 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -170,18 +170,7 @@ llvm::Function* Arith256::getDivFunc(llvm::Type* _type) auto yLz = m_builder.CreateCall2(ctlzIntr, yArg, m_builder.getInt1(true), "y.lz"); auto rLz = m_builder.CreateCall2(ctlzIntr, r0, m_builder.getInt1(true), "r.lz"); auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0"); - auto shlBy0 = m_builder.CreateICmpEQ(i0, zero); auto y0 = m_builder.CreateShl(yArg, i0); - if (_type == m_builder.getIntNTy(512)) // Workaround for shl bug for long shifts - { - const auto treshold = m_builder.getIntN(512, 128); - auto highShift = m_builder.CreateICmpUGT(i0, treshold); - auto s = m_builder.CreateNUWSub(i0, treshold); - auto yhs = m_builder.CreateShl(yArg, treshold); - yhs = m_builder.CreateShl(yhs, s); - y0 = m_builder.CreateSelect(highShift, yhs, y0); - } - y0 = m_builder.CreateSelect(shlBy0, yArg, y0, "y0"); // Workaround for LLVM bug: shl by 0 produces wrong result m_builder.CreateBr(loopBB); m_builder.SetInsertPoint(loopBB); diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 1e1f8fe93..d5cb493ab 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -464,12 +464,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti // test for word >> (k * 8 + 7) auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos"); auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word); - auto bittester = m_builder.CreateShl(Constant::get(1), bitposEx); - auto bitresult = m_builder.CreateAnd(word, bittester); - auto bittest = m_builder.CreateICmpUGT(bitresult, Constant::get(0)); - // FIXME: The following does not work - LLVM bug, report! - //auto bitval = m_builder.CreateLShr(word, bitpos, "bitval"); - //auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest"); + auto bitval = m_builder.CreateLShr(word, bitposEx, "bitval"); + auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest"); auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx); auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); diff --git a/libevmjit/Endianness.cpp b/libevmjit/Endianness.cpp index d36f4b7fa..25b66c0d5 100644 --- a/libevmjit/Endianness.cpp +++ b/libevmjit/Endianness.cpp @@ -18,9 +18,8 @@ llvm::Value* Endianness::bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _wo { if (llvm::sys::IsLittleEndianHost) { - // FIXME: Disabled because of problems with BYTE - //if (auto constant = llvm::dyn_cast(_word)) - // return _builder.getInt(constant->getValue().byteSwap()); + if (auto constant = llvm::dyn_cast(_word)) + return _builder.getInt(constant->getValue().byteSwap()); // OPT: Cache func declaration? auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::Word); diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index 9bd767158..fba9cfd96 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -85,20 +85,7 @@ void parseOptions() { static llvm::llvm_shutdown_obj shutdownObj{}; cl::AddExtraVersionPrinter(printVersion); - //cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); - - // FIXME: LLVM workaround: - // Manually select instruction scheduler. Confirmed bad schedulers: source, list-burr, list-hybrid. - // "source" scheduler has a bug: http://llvm.org/bugs/show_bug.cgi?id=22304 - auto envLine = std::getenv("EVMJIT"); - auto commandLine = std::string{"evmjit "} + (envLine ? envLine : "") + " -pre-RA-sched=list-ilp\0"; - static const auto c_maxArgs = 20; - char const* argv[c_maxArgs] = {nullptr, }; - auto arg = std::strtok(&*commandLine.begin(), " "); - auto i = 0; - for (; i < c_maxArgs && arg; ++i, arg = std::strtok(nullptr, " ")) - argv[i] = arg; - cl::ParseCommandLineOptions(i, argv, "Ethereum EVM JIT Compiler"); + cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); } } diff --git a/libevmjit/GasMeter.cpp b/libevmjit/GasMeter.cpp index ffbd654e6..c0a2ed287 100644 --- a/libevmjit/GasMeter.cpp +++ b/libevmjit/GasMeter.cpp @@ -216,22 +216,12 @@ void GasMeter::countExp(llvm::Value* _exponent) // cost = ((256 - lz) + 7) / 8 // OPT: Can gas update be done in exp algorithm? - - auto t = llvm::APInt{256, 1}; - auto c = m_builder.CreateSelect(m_builder.CreateICmpUGE(_exponent, Constant::get(t)), m_builder.getInt64(1), m_builder.getInt64(0)); - for (auto i = 2; i <= 32; ++i) - { - t <<= 8; - c = m_builder.CreateSelect(m_builder.CreateICmpUGE(_exponent, Constant::get(t)), m_builder.getInt64(i), c); - } - - // FIXME: Does not work because of LLVM bug: https://llvm.org/bugs/show_bug.cgi?id=22304 -// auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); -// auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false)); -// auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz"); -// auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits"); -// auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8)); - count(m_builder.CreateNUWMul(c, m_builder.getInt64(c_expByteGas))); + auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); + auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false)); + auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz"); + auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits"); + auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8)); + count(m_builder.CreateNUWMul(sigBytes, m_builder.getInt64(c_expByteGas))); } void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue) diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index 731fda5c6..df88b4df8 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -16,9 +16,9 @@ namespace jit bool optimize(llvm::Module& _module) { auto pm = llvm::legacy::PassManager{}; - //pm.add(llvm::createFunctionInliningPass(2, 2)); // Produces invalid IR + //pm.add(llvm::createFunctionInliningPass(2, 2)); // Problem with APInt value bigger than 64bit pm.add(llvm::createCFGSimplificationPass()); - //pm.add(llvm::createInstructionCombiningPass()); // Produces invalid runtime results + pm.add(llvm::createInstructionCombiningPass()); pm.add(llvm::createAggressiveDCEPass()); pm.add(llvm::createLowerSwitchPass()); return pm.run(_module); diff --git a/libevmjit/preprocessor/llvm_includes_end.h b/libevmjit/preprocessor/llvm_includes_end.h index 2ead6dda3..643e03064 100644 --- a/libevmjit/preprocessor/llvm_includes_end.h +++ b/libevmjit/preprocessor/llvm_includes_end.h @@ -1,7 +1,3 @@ #if defined(_MSC_VER) #pragma warning(pop) -#elif defined(__clang__) - #pragma clang diagnostic pop -#else - #pragma GCC diagnostic pop #endif diff --git a/libevmjit/preprocessor/llvm_includes_start.h b/libevmjit/preprocessor/llvm_includes_start.h index 9077bf43f..9499f9835 100644 --- a/libevmjit/preprocessor/llvm_includes_start.h +++ b/libevmjit/preprocessor/llvm_includes_start.h @@ -1,12 +1,4 @@ #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4267 4244 4800) -#elif defined(__clang__) - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wunused-parameter" - #pragma clang diagnostic ignored "-Wconversion" -#else - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wunused-parameter" - #pragma GCC diagnostic ignored "-Wconversion" #endif From b4960f4f42f0f60ad1624f96325654f94d90da1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 23 Apr 2015 12:16:07 +0200 Subject: [PATCH 03/72] Add support for building with llvm-3.7-deb Debian package --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ab394a80..35332f232 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,12 +25,12 @@ if(LLVM_DIR OR APPLE) # local LLVM build llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen bitwriter ipo) else() # Workaround for Ubuntu broken LLVM package - message(STATUS "Using llvm-3.5-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake") - execute_process(COMMAND llvm-config-3.5 --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS) + message(STATUS "Using llvm-3.7-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake") + execute_process(COMMAND llvm-config-3.7 --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") + set(LLVM_LIBS "-lLLVMBitWriter -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMX86AsmParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMMCJIT -lLLVMExecutionEngine -lLLVMRuntimeDyld -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMMC -lLLVMMCDisassembler -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) + link_directories(/usr/lib/llvm-3.7/lib) endif() add_subdirectory(libevmjit) From d6f4eb1eef52ad698a4a9d51844d86fc6080a133 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 24 Apr 2015 17:51:25 +0200 Subject: [PATCH 04/72] Statically compile libevmjit-cpp. --- libevmjit-cpp/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmjit-cpp/CMakeLists.txt b/libevmjit-cpp/CMakeLists.txt index add132f2a..5b4dbb9c8 100644 --- a/libevmjit-cpp/CMakeLists.txt +++ b/libevmjit-cpp/CMakeLists.txt @@ -15,7 +15,7 @@ else() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # add PIC for archive endif() -add_library(${TARGET_NAME} ${SOURCES}) +add_library(${TARGET_NAME} STATIC ${SOURCES}) set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") include_directories(../..) From f846adf915a589fcee8aa7c4c9db91c185c02ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Apr 2015 12:56:01 +0200 Subject: [PATCH 05/72] Revert not needed LLVM workarounds --- libevmjit/Arith256.cpp | 104 ++++++++++++++++++++--------------------- libevmjit/Compiler.cpp | 13 ++---- 2 files changed, 55 insertions(+), 62 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index 029eb9370..c7608c5b8 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -360,18 +360,17 @@ llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2) std::pair Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) { - // FIXME: Disabled because of llvm::APInt::urem bug -// if (auto c1 = llvm::dyn_cast(_arg1)) -// { -// if (auto c2 = llvm::dyn_cast(_arg2)) -// { -// if (!c2->getValue()) -// return std::make_pair(Constant::get(0), Constant::get(0)); -// auto div = Constant::get(c1->getValue().udiv(c2->getValue())); -// auto mod = Constant::get(c1->getValue().urem(c2->getValue())); -// return std::make_pair(div, mod); -// } -// } + if (auto c1 = llvm::dyn_cast(_arg1)) + { + if (auto c2 = llvm::dyn_cast(_arg2)) + { + if (!c2->getValue()) + return std::make_pair(Constant::get(0), Constant::get(0)); + auto div = Constant::get(c1->getValue().udiv(c2->getValue())); + auto mod = Constant::get(c1->getValue().urem(c2->getValue())); + return std::make_pair(div, mod); + } + } auto r = createCall(getDivFunc(Type::Word), {_arg1, _arg2}); auto div = m_builder.CreateExtractValue(r, 0, "div"); @@ -381,18 +380,17 @@ std::pair Arith256::div(llvm::Value* _arg1, llvm::Va std::pair Arith256::sdiv(llvm::Value* _x, llvm::Value* _y) { - // FIXME: Disabled because of llvm::APInt::urem bug -// if (auto c1 = llvm::dyn_cast(_x)) -// { -// if (auto c2 = llvm::dyn_cast(_y)) -// { -// if (!c2->getValue()) -// return std::make_pair(Constant::get(0), Constant::get(0)); -// auto div = Constant::get(c1->getValue().sdiv(c2->getValue())); -// auto mod = Constant::get(c1->getValue().srem(c2->getValue())); -// return std::make_pair(div, mod); -// } -// } + if (auto c1 = llvm::dyn_cast(_x)) + { + if (auto c2 = llvm::dyn_cast(_y)) + { + if (!c2->getValue()) + return std::make_pair(Constant::get(0), Constant::get(0)); + auto div = Constant::get(c1->getValue().sdiv(c2->getValue())); + auto mod = Constant::get(c1->getValue().srem(c2->getValue())); + return std::make_pair(div, mod); + } + } auto xIsNeg = m_builder.CreateICmpSLT(_x, Constant::get(0)); auto xNeg = m_builder.CreateSub(Constant::get(0), _x); @@ -447,42 +445,40 @@ llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) { - // FIXME: Disabled because of llvm::APInt::urem bug -// if (auto c1 = llvm::dyn_cast(_arg1)) -// { -// if (auto c2 = llvm::dyn_cast(_arg2)) -// { -// if (auto c3 = llvm::dyn_cast(_arg3)) -// { -// if (!c3->getValue()) -// return Constant::get(0); -// auto s = c1->getValue().zext(256+64) + c2->getValue().zext(256+64); -// auto r = s.urem(c3->getValue().zext(256+64)).trunc(256); -// return Constant::get(r); -// } -// } -// } + if (auto c1 = llvm::dyn_cast(_arg1)) + { + if (auto c2 = llvm::dyn_cast(_arg2)) + { + if (auto c3 = llvm::dyn_cast(_arg3)) + { + if (!c3->getValue()) + return Constant::get(0); + auto s = c1->getValue().zext(256+64) + c2->getValue().zext(256+64); + auto r = s.urem(c3->getValue().zext(256+64)).trunc(256); + return Constant::get(r); + } + } + } return createCall(getAddModFunc(), {_arg1, _arg2, _arg3}); } llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) { - // FIXME: Disabled because of llvm::APInt::urem bug -// if (auto c1 = llvm::dyn_cast(_arg1)) -// { -// if (auto c2 = llvm::dyn_cast(_arg2)) -// { -// if (auto c3 = llvm::dyn_cast(_arg3)) -// { -// if (!c3->getValue()) -// return Constant::get(0); -// auto p = c1->getValue().zext(512) * c2->getValue().zext(512); -// auto r = p.urem(c3->getValue().zext(512)).trunc(256); -// return Constant::get(r); -// } -// } -// } + if (auto c1 = llvm::dyn_cast(_arg1)) + { + if (auto c2 = llvm::dyn_cast(_arg2)) + { + if (auto c3 = llvm::dyn_cast(_arg3)) + { + if (!c3->getValue()) + return Constant::get(0); + auto p = c1->getValue().zext(512) * c2->getValue().zext(512); + auto r = p.urem(c3->getValue().zext(512)).trunc(256); + return Constant::get(r); + } + } + } return createCall(getMulModFunc(), {_arg1, _arg2, _arg3}); } diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index d5cb493ab..25f5932df 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -417,17 +417,14 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti case Instruction::BYTE: { - const auto byteNum = stack.pop(); - auto value = stack.pop(); + const auto idx = stack.pop(); + auto value = Endianness::toBE(m_builder, stack.pop()); - value = Endianness::toBE(m_builder, value); + auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid"); auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); - 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"); + auto byte = m_builder.CreateExtractElement(bytes, idx, "byte"); value = m_builder.CreateZExt(byte, Type::Word); - - auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32)); - value = m_builder.CreateSelect(byteNumValid, value, Constant::get(0)); + value = m_builder.CreateSelect(idxValid, value, Constant::get(0)); stack.push(value); break; } From 228db6bc074dd430af89876f42c0ef5927bae675 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Apr 2015 13:34:16 +0200 Subject: [PATCH 06/72] Remove unused stuff --- libevmjit/Compiler.cpp | 9 ++------- libevmjit/CompilerHelper.h | 25 +------------------------ libevmjit/Memory.cpp | 5 ++--- libevmjit/RuntimeManager.cpp | 3 +-- libevmjit/Type.cpp | 3 --- libevmjit/Type.h | 4 ---- 6 files changed, 6 insertions(+), 43 deletions(-) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 25f5932df..92a2b0e0c 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -455,7 +455,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto word = stack.pop(); auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32"); - auto k32 = m_builder.CreateZExt(k32_, Type::lowPrecision); + auto k32 = m_builder.CreateZExt(k32_, Type::Size); auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8"); // test for word >> (k * 8 + 7) @@ -492,11 +492,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti case Instruction::POP: { - auto val = stack.pop(); - static_cast(val); - // Generate a dummy use of val to make sure that a get(0) will be emitted at this point, - // so that StackUnderflow will be thrown - // m_builder.CreateICmpEQ(val, val, "dummy"); + stack.pop(); break; } @@ -653,7 +649,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti } case Instruction::CODESIZE: - // TODO: Use constant stack.push(_runtimeManager.getCodeSize()); break; diff --git a/libevmjit/CompilerHelper.h b/libevmjit/CompilerHelper.h index cd6d09a58..5eeae9a6e 100644 --- a/libevmjit/CompilerHelper.h +++ b/libevmjit/CompilerHelper.h @@ -37,7 +37,6 @@ protected: friend class RuntimeHelper; }; - /// Compiler helper that depends on runtime data class RuntimeHelper : public CompilerHelper { @@ -50,29 +49,7 @@ private: RuntimeManager& m_runtimeManager; }; - -/// Saves the insert point of the IR builder and restores it when destructed -struct InsertPointGuard -{ - InsertPointGuard(llvm::IRBuilder<>& _builder) : - m_builder(_builder), - m_insertBB(m_builder.GetInsertBlock()), - m_insertPt(m_builder.GetInsertPoint()) - {} - - InsertPointGuard(const InsertPointGuard&) = delete; - void operator=(InsertPointGuard) = delete; - - ~InsertPointGuard() - { - m_builder.SetInsertPoint(m_insertBB, m_insertPt); - } - -private: - llvm::IRBuilder<>& m_builder; - llvm::BasicBlock* m_insertBB; - llvm::BasicBlock::iterator m_insertPt; -}; +using InsertPointGuard = llvm::IRBuilderBase::InsertPointGuard; } } diff --git a/libevmjit/Memory.cpp b/libevmjit/Memory.cpp index 79a82a8ae..6d5b72024 100644 --- a/libevmjit/Memory.cpp +++ b/libevmjit/Memory.cpp @@ -191,8 +191,7 @@ llvm::Value* Memory::getSize() llvm::Value* Memory::getBytePtr(llvm::Value* _index) { - auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 - return m_builder.CreateGEP(getData(), idx, "ptr"); + return m_builder.CreateGEP(getData(), _index, "ptr"); } void Memory::require(llvm::Value* _offset, llvm::Value* _size) @@ -235,7 +234,7 @@ void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value* auto bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero"); auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); - auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64 + auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); auto padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx"); auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx); auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx); diff --git a/libevmjit/RuntimeManager.cpp b/libevmjit/RuntimeManager.cpp index 5c66a5a21..8d000f4c8 100644 --- a/libevmjit/RuntimeManager.cpp +++ b/libevmjit/RuntimeManager.cpp @@ -194,8 +194,7 @@ void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size { auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo()); auto mem = getBuilder().CreateLoad(memPtr, "memory"); - auto idx = m_builder.CreateTrunc(_offset, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 // TODO: Report bug & fix to LLVM - auto returnDataPtr = getBuilder().CreateGEP(mem, idx); + auto returnDataPtr = getBuilder().CreateGEP(mem, _offset); set(RuntimeData::ReturnData, returnDataPtr); auto size64 = getBuilder().CreateTrunc(_size, Type::Size); diff --git a/libevmjit/Type.cpp b/libevmjit/Type.cpp index 00a143dea..6ac9a6467 100644 --- a/libevmjit/Type.cpp +++ b/libevmjit/Type.cpp @@ -13,7 +13,6 @@ namespace jit llvm::IntegerType* Type::Word; llvm::PointerType* Type::WordPtr; -llvm::IntegerType* Type::lowPrecision; llvm::IntegerType* Type::Bool; llvm::IntegerType* Type::Size; llvm::IntegerType* Type::Gas; @@ -34,8 +33,6 @@ void Type::init(llvm::LLVMContext& _context) { Word = llvm::Type::getIntNTy(_context, 256); WordPtr = Word->getPointerTo(); - lowPrecision = llvm::Type::getInt64Ty(_context); - // TODO: Size should be architecture-dependent Bool = llvm::Type::getInt1Ty(_context); Size = llvm::Type::getInt64Ty(_context); Gas = Size; diff --git a/libevmjit/Type.h b/libevmjit/Type.h index fcca98d74..ba9181f6b 100644 --- a/libevmjit/Type.h +++ b/libevmjit/Type.h @@ -20,10 +20,6 @@ struct Type static llvm::IntegerType* Word; static llvm::PointerType* WordPtr; - /// Type for doing low precision arithmetics where 256-bit precision is not supported by native target - /// @TODO: Use 64-bit for now. In 128-bit compiler-rt library functions are required - static llvm::IntegerType* lowPrecision; - static llvm::IntegerType* Bool; static llvm::IntegerType* Size; static llvm::IntegerType* Gas; From f1d8fbefbf2788f3081b1595fe42f5159c3d8405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 27 Apr 2015 16:24:57 +0200 Subject: [PATCH 07/72] Move calldataload procedure to LLVM IR --- libevmjit/Compiler.cpp | 4 ++-- libevmjit/Ext.cpp | 23 ++++++++++++++++++----- libevmjit/Ext.h | 1 - libevmjit/Stack.cpp | 29 ----------------------------- 4 files changed, 20 insertions(+), 37 deletions(-) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 92a2b0e0c..d7240a415 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -721,8 +721,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti case Instruction::CALLDATALOAD: { - auto index = stack.pop(); - auto value = _ext.calldataload(index); + auto idx = stack.pop(); + auto value = _ext.calldataload(idx); stack.push(value); break; } diff --git a/libevmjit/Ext.cpp b/libevmjit/Ext.cpp index d35aebc68..9b1e0983b 100644 --- a/libevmjit/Ext.cpp +++ b/libevmjit/Ext.cpp @@ -45,7 +45,6 @@ std::array::value> const& getEnvFuncDescs() FuncDesc{"env_log", getFunctionType(Type::Void, {Type::EnvPtr, Type::BytePtr, Type::Size, Type::WordPtr, Type::WordPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})}, FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, - FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})}, }}; return descs; @@ -101,12 +100,26 @@ void Ext::sstore(llvm::Value* _index, llvm::Value* _value) createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness } -llvm::Value* Ext::calldataload(llvm::Value* _index) +llvm::Value* Ext::calldataload(llvm::Value* _idx) { auto ret = getArgAlloca(); - createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret}); - ret = m_builder.CreateLoad(ret); - return Endianness::toNative(m_builder, ret); + auto result = m_builder.CreateBitCast(ret, Type::BytePtr); + + auto callDataSize = getRuntimeManager().getCallDataSize(); + auto callDataSize64 = m_builder.CreateTrunc(callDataSize, Type::Size); + auto idxValid = m_builder.CreateICmpULT(_idx, callDataSize); + auto idx = m_builder.CreateTrunc(m_builder.CreateSelect(idxValid, _idx, callDataSize), Type::Size, "idx"); + + auto end = m_builder.CreateNUWAdd(idx, m_builder.getInt64(32)); + end = m_builder.CreateSelect(m_builder.CreateICmpULE(end, callDataSize64), end, callDataSize64); + auto copySize = m_builder.CreateNUWSub(end, idx); + auto padSize = m_builder.CreateNUWSub(m_builder.getInt64(32), copySize); + auto dataBegin = m_builder.CreateGEP(Type::Byte, getRuntimeManager().getCallData(), idx); + m_builder.CreateMemCpy(result, dataBegin, copySize, 1); + auto pad = m_builder.CreateGEP(Type::Byte, result, copySize); + m_builder.CreateMemSet(pad, m_builder.getInt8(0), padSize, 1); + + return Endianness::toNative(m_builder, m_builder.CreateLoad(ret)); } llvm::Value* Ext::balance(llvm::Value* _address) diff --git a/libevmjit/Ext.h b/libevmjit/Ext.h index b0048d2e9..760f77df5 100644 --- a/libevmjit/Ext.h +++ b/libevmjit/Ext.h @@ -35,7 +35,6 @@ enum class EnvFunc log, blockhash, extcode, - calldataload, // Helper function, not client Env interface _size }; diff --git a/libevmjit/Stack.cpp b/libevmjit/Stack.cpp index 3a686c766..7cf514dea 100644 --- a/libevmjit/Stack.cpp +++ b/libevmjit/Stack.cpp @@ -175,32 +175,3 @@ void Stack::push(llvm::Value* _value) } } } - -extern "C" -{ - using namespace dev::eth::jit; - - EXPORT void ext_calldataload(RuntimeData* _rtData, i256* _index, byte* o_value) - { - // It asumes all indexes are less than 2^64 - - auto index = _index->a; - if (_index->b || _index->c || _index->d) // if bigger that 2^64 - index = std::numeric_limits::max(); // set max to fill with 0 leter - - auto data = _rtData->callData; - auto size = _rtData->callDataSize; - for (auto i = 0; i < 32; ++i) - { - if (index < size) - { - o_value[i] = data[index]; - ++index; // increment only if in range - } - else - o_value[i] = 0; - } - } - -} // extern "C" - From 463025e15de5181a9661e6e67876588908bb8eb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 28 Apr 2015 17:50:01 +0200 Subject: [PATCH 08/72] Implementation of MUL workaround in "LLVM pass" way. --- libevmjit/Arith256.cpp | 125 ++++++++++++++++------------------ libevmjit/Arith256.h | 5 +- libevmjit/Compiler.cpp | 2 +- libevmjit/ExecutionEngine.cpp | 2 + libevmjit/Optimizer.cpp | 59 ++++++++++++++++ libevmjit/Optimizer.h | 2 + 6 files changed, 124 insertions(+), 71 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index c7608c5b8..d8e4c5138 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -4,6 +4,7 @@ #include #include "preprocessor/llvm_includes_start.h" +#include #include #include "preprocessor/llvm_includes_end.h" @@ -32,57 +33,56 @@ void Arith256::debug(llvm::Value* _value, char _c) createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_c)}); } -llvm::Function* Arith256::getMulFunc() +llvm::Function* Arith256::getMulFunc(llvm::Module& _module) { - auto& func = m_mul; - if (!func) - { - llvm::Type* argTypes[] = {Type::Word, Type::Word}; - func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule()); - func->setDoesNotThrow(); - func->setDoesNotAccessMemory(); - - auto x = &func->getArgumentList().front(); - x->setName("x"); - auto y = x->getNextNode(); - y->setName("y"); - - InsertPointGuard guard{m_builder}; - auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func); - m_builder.SetInsertPoint(bb); - auto i64 = Type::Size; - auto i128 = m_builder.getIntNTy(128); - auto i256 = Type::Word; - auto c64 = Constant::get(64); - auto c128 = Constant::get(128); - auto c192 = Constant::get(192); - - auto x_lo = m_builder.CreateTrunc(x, i64, "x.lo"); - auto y_lo = m_builder.CreateTrunc(y, i64, "y.lo"); - auto x_mi = m_builder.CreateTrunc(m_builder.CreateLShr(x, c64), i64); - auto y_mi = m_builder.CreateTrunc(m_builder.CreateLShr(y, c64), i64); - auto x_hi = m_builder.CreateTrunc(m_builder.CreateLShr(x, c128), i128); - auto y_hi = m_builder.CreateTrunc(m_builder.CreateLShr(y, c128), i128); - - auto t1 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_lo, i128)); - auto t2 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_mi, i128)); - auto t3 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), y_hi); - auto t4 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_lo, i128)); - auto t5 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_mi, i128)); - auto t6 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), y_hi); - auto t7 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_lo, i128)); - auto t8 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_mi, i128)); - - auto p = m_builder.CreateZExt(t1, i256); - p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i256), c64)); - p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i256), c128)); - p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i256), c64)); - p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t5, i256), c128)); - p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t6, i256), c192)); - p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t7, i256), c128)); - p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t8, i256), c192)); - m_builder.CreateRet(p); - } + static const auto funcName = "evm.mul.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + llvm::Type* argTypes[] = {Type::Word, Type::Word}; + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); + auto builder = llvm::IRBuilder<>{bb}; + auto i64 = Type::Size; + auto i128 = builder.getIntNTy(128); + auto i256 = Type::Word; + auto c64 = Constant::get(64); + auto c128 = Constant::get(128); + auto c192 = Constant::get(192); + + auto x_lo = builder.CreateTrunc(x, i64, "x.lo"); + auto y_lo = builder.CreateTrunc(y, i64, "y.lo"); + auto x_mi = builder.CreateTrunc(builder.CreateLShr(x, c64), i64); + auto y_mi = builder.CreateTrunc(builder.CreateLShr(y, c64), i64); + auto x_hi = builder.CreateTrunc(builder.CreateLShr(x, c128), i128); + auto y_hi = builder.CreateTrunc(builder.CreateLShr(y, c128), i128); + + auto t1 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_lo, i128)); + auto t2 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_mi, i128)); + auto t3 = builder.CreateMul(builder.CreateZExt(x_lo, i128), y_hi); + auto t4 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_lo, i128)); + auto t5 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_mi, i128)); + auto t6 = builder.CreateMul(builder.CreateZExt(x_mi, i128), y_hi); + auto t7 = builder.CreateMul(x_hi, builder.CreateZExt(y_lo, i128)); + auto t8 = builder.CreateMul(x_hi, builder.CreateZExt(y_mi, i128)); + + auto p = builder.CreateZExt(t1, i256); + p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i256), c64)); + p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i256), c128)); + p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i256), c64)); + p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t5, i256), c128)); + p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t6, i256), c192)); + p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t7, i256), c128)); + p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t8, i256), c192)); + builder.CreateRet(p); return func; } @@ -112,10 +112,11 @@ llvm::Function* Arith256::getMul512Func() auto x_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256); auto y_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256); - auto t1 = createCall(getMulFunc(), {x_lo, y_lo}); - auto t2 = createCall(getMulFunc(), {x_lo, y_hi}); - auto t3 = createCall(getMulFunc(), {x_hi, y_lo}); - auto t4 = createCall(getMulFunc(), {x_hi, y_hi}); + auto mul256Func = getMulFunc(*getModule()); + auto t1 = createCall(mul256Func, {x_lo, y_lo}); + auto t2 = createCall(mul256Func, {x_lo, y_hi}); + auto t3 = createCall(mul256Func, {x_hi, y_lo}); + auto t4 = createCall(mul256Func, {x_hi, y_hi}); auto p = m_builder.CreateZExt(t1, i512); p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i512), m_builder.getIntN(512, 128))); @@ -260,14 +261,15 @@ llvm::Function* Arith256::getExpFunc() m_builder.CreateCondBr(eOdd, updateBB, continueBB); m_builder.SetInsertPoint(updateBB); - auto r0 = createCall(getMulFunc(), {r, b}); + auto mul256Func = getMulFunc(*getModule()); + auto r0 = createCall(mul256Func, {r, b}); m_builder.CreateBr(continueBB); m_builder.SetInsertPoint(continueBB); auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); r1->addIncoming(r, bodyBB); r1->addIncoming(r0, updateBB); - auto b1 = createCall(getMulFunc(), {b, b}); + auto b1 = createCall(mul256Func, {b, b}); auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1"); m_builder.CreateBr(headerBB); @@ -347,17 +349,6 @@ llvm::Function* Arith256::getMulModFunc() return m_mulmod; } -llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2) -{ - if (auto c1 = llvm::dyn_cast(_arg1)) - { - if (auto c2 = llvm::dyn_cast(_arg2)) - return Constant::get(c1->getValue() * c2->getValue()); - } - - return createCall(getMulFunc(), {_arg1, _arg2}); -} - std::pair Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) { if (auto c1 = llvm::dyn_cast(_arg1)) diff --git a/libevmjit/Arith256.h b/libevmjit/Arith256.h index 2513ca568..a5762429d 100644 --- a/libevmjit/Arith256.h +++ b/libevmjit/Arith256.h @@ -14,7 +14,6 @@ class Arith256 : public CompilerHelper public: Arith256(llvm::IRBuilder<>& _builder); - llvm::Value* mul(llvm::Value* _arg1, llvm::Value* _arg2); std::pair div(llvm::Value* _arg1, llvm::Value* _arg2); std::pair sdiv(llvm::Value* _arg1, llvm::Value* _arg2); llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); @@ -23,15 +22,15 @@ public: void debug(llvm::Value* _value, char _c); + static llvm::Function* getMulFunc(llvm::Module& _module); + private: - llvm::Function* getMulFunc(); llvm::Function* getMul512Func(); llvm::Function* getDivFunc(llvm::Type* _type); llvm::Function* getExpFunc(); llvm::Function* getAddModFunc(); llvm::Function* getMulModFunc(); - llvm::Function* m_mul = nullptr; llvm::Function* m_mul512 = nullptr; llvm::Function* m_div = nullptr; llvm::Function* m_div512 = nullptr; diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index d7240a415..a2f58c770 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -270,7 +270,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti { auto lhs = stack.pop(); auto rhs = stack.pop(); - auto res = _arith.mul(lhs, rhs); + auto res = m_builder.CreateMul(lhs, rhs); stack.push(res); break; } diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index fba9cfd96..b7cf41010 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -162,6 +162,8 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) listener->stateChanged(ExecState::Optimization); optimize(*module); } + + prepare(*module); } if (g_dump) module->dump(); diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index df88b4df8..4913dcaa0 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -1,11 +1,16 @@ #include "Optimizer.h" #include "preprocessor/llvm_includes_start.h" +#include +#include #include #include #include #include "preprocessor/llvm_includes_end.h" +#include "Arith256.h" +#include "Type.h" + namespace dev { namespace eth @@ -24,6 +29,60 @@ bool optimize(llvm::Module& _module) return pm.run(_module); } +namespace +{ + +class LowerEVMPass : public llvm::BasicBlockPass +{ + static char ID; + + bool m_mulFuncNeeded = false; + +public: + LowerEVMPass(): + llvm::BasicBlockPass(ID) + {} + + virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override; + + virtual bool doFinalization(llvm::Module& _module) override; +}; + +char LowerEVMPass::ID = 0; + +bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb) +{ + auto modified = false; + auto module = _bb.getParent()->getParent(); + for (auto&& inst : _bb) + { + if (inst.getOpcode() == llvm::Instruction::Mul) + { + if (inst.getType() == Type::Word) + { + auto call = llvm::CallInst::Create(Arith256::getMulFunc(*module), {inst.getOperand(0), inst.getOperand(1)}, "", &inst); + inst.replaceAllUsesWith(call); + modified = true; + } + } + } + return modified; +} + +bool LowerEVMPass::doFinalization(llvm::Module&) +{ + return false; +} + +} + +bool prepare(llvm::Module& _module) +{ + auto pm = llvm::legacy::PassManager{}; + pm.add(new LowerEVMPass{}); + return pm.run(_module); +} + } } } diff --git a/libevmjit/Optimizer.h b/libevmjit/Optimizer.h index 4a3147a7f..4b7ab7e9a 100644 --- a/libevmjit/Optimizer.h +++ b/libevmjit/Optimizer.h @@ -14,6 +14,8 @@ namespace jit bool optimize(llvm::Module& _module); +bool prepare(llvm::Module& _module); + } } } From c3fe9ad112a69c9ae42a25914eda8315d2102338 Mon Sep 17 00:00:00 2001 From: chriseth Date: Fri, 24 Apr 2015 17:35:16 +0200 Subject: [PATCH 09/72] Move assembly related files to libevmasm and Params.h/.cpp to libevmcore. --- libevmjit-cpp/Env.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index b89aca726..2c37412fc 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -1,7 +1,7 @@ #pragma GCC diagnostic ignored "-Wconversion" #include -#include +#include #include #include "Utils.h" From d97217dcbc824541b1a231737d49e68ca5b744c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 4 May 2015 12:34:38 +0200 Subject: [PATCH 10/72] Return divrem results using vector <2 x i256> --- libevmjit/Arith256.cpp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index d8e4c5138..107eb5b05 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -137,7 +137,7 @@ llvm::Function* Arith256::getDivFunc(llvm::Type* _type) // The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder llvm::Type* argTypes[] = {_type, _type}; - auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef{argTypes}); + auto retType = llvm::VectorType::get(_type, 2); auto funcName = _type == Type::Word ? "div" : "div512"; func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule()); func->setDoesNotThrow(); @@ -209,8 +209,8 @@ llvm::Function* Arith256::getDivFunc(llvm::Type* _type) auto rRet = m_builder.CreatePHI(_type, 2, "r.ret"); rRet->addIncoming(r0, entryBB); rRet->addIncoming(r1, loopBB); - auto ret = m_builder.CreateInsertValue(llvm::UndefValue::get(retType), qRet, 0, "ret0"); - ret = m_builder.CreateInsertValue(ret, rRet, 1, "ret"); + auto ret = m_builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0"); + ret = m_builder.CreateInsertElement(ret, rRet, 1, "ret"); m_builder.CreateRet(ret); } return func; @@ -312,7 +312,7 @@ llvm::Function* Arith256::getAddModFunc() auto m512 = m_builder.CreateZExt(mod, i512Ty, "m512"); auto s = m_builder.CreateAdd(x512, y512, "s"); auto d = createCall(getDivFunc(i512Ty), {s, m512}); - auto r = m_builder.CreateExtractValue(d, 1, "r"); + auto r = m_builder.CreateExtractElement(d, 1, "r"); m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word)); } return m_addmod; @@ -342,9 +342,8 @@ llvm::Function* Arith256::getMulModFunc() auto p = createCall(getMul512Func(), {x, y}); auto m = m_builder.CreateZExt(mod, i512Ty, "m"); auto d = createCall(getDivFunc(i512Ty), {p, m}); - auto r = m_builder.CreateExtractValue(d, 1, "r"); - r = m_builder.CreateTrunc(r, Type::Word); - m_builder.CreateRet(r); + auto r = m_builder.CreateExtractElement(d, 1, "r"); + m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word)); } return m_mulmod; } @@ -364,8 +363,8 @@ std::pair Arith256::div(llvm::Value* _arg1, llvm::Va } auto r = createCall(getDivFunc(Type::Word), {_arg1, _arg2}); - auto div = m_builder.CreateExtractValue(r, 0, "div"); - auto mod = m_builder.CreateExtractValue(r, 1, "mod"); + auto div = m_builder.CreateExtractElement(r, uint64_t(0), "div"); + auto mod = m_builder.CreateExtractElement(r, 1, "mod"); return std::make_pair(div, mod); } From 9ca47fe0cf795271cb6e86e5df612bd4c5b3b49f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 5 May 2015 17:52:27 +0200 Subject: [PATCH 11/72] JIT class: the EVM JIT facade. The JIT class added, future public EVM JIT library interface. Currently it supports queries about EVM code status. --- CMakeLists.txt | 2 ++ include/evmjit/DataTypes.h | 56 +++++++++++++++++++++++++++++++++++ include/evmjit/JIT.h | 36 ++++++++++++++++++++++ libevmjit-cpp/CMakeLists.txt | 1 + libevmjit-cpp/Env.cpp | 3 +- libevmjit-cpp/Utils.h | 14 ++++++--- libevmjit/CMakeLists.txt | 3 ++ libevmjit/Common.h | 12 +------- libevmjit/ExecutionEngine.cpp | 24 +++++++-------- libevmjit/JIT.cpp | 46 ++++++++++++++++++++++++++++ libevmjit/RuntimeData.h | 7 +++-- 11 files changed, 172 insertions(+), 32 deletions(-) create mode 100644 include/evmjit/DataTypes.h create mode 100644 include/evmjit/JIT.h create mode 100644 libevmjit/JIT.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5ab394a80..a0e9c1f46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,8 @@ else() link_directories(/usr/lib/llvm-3.5/lib) endif() +get_filename_component(EVMJIT_INCLUDE_DIR include ABSOLUTE) + add_subdirectory(libevmjit) if(EVMJIT_CPP) diff --git a/include/evmjit/DataTypes.h b/include/evmjit/DataTypes.h new file mode 100644 index 000000000..179d9a372 --- /dev/null +++ b/include/evmjit/DataTypes.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include + +namespace dev +{ +namespace evmjit +{ + +struct h256 +{ + uint64_t words[4]; +}; + +inline bool operator==(h256 _h1, h256 _h2) +{ + return _h1.words[0] == _h2.words[0] && + _h1.words[1] == _h2.words[1] && + _h1.words[2] == _h2.words[2] && + _h1.words[3] == _h2.words[3]; +} + +/// Representation of 256-bit value binary compatible with LLVM i256 +struct i256 +{ + uint64_t a = 0; + uint64_t b = 0; + uint64_t c = 0; + uint64_t d = 0; + + i256() = default; + i256(h256 _h) + { + a = _h.words[0]; + b = _h.words[1]; + c = _h.words[2]; + d = _h.words[3]; + } +}; + +} +} + +namespace std +{ +template<> struct hash +{ + size_t operator()(dev::evmjit::h256 const& _h) const + { + /// This implementation expects the argument to be a full 256-bit Keccak hash. + /// It does nothing more than returning a slice of the input hash. + return static_cast(_h.words[0]); + }; +}; +} diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h new file mode 100644 index 000000000..446dd9e56 --- /dev/null +++ b/include/evmjit/JIT.h @@ -0,0 +1,36 @@ +#pragma once + +#include "evmjit/DataTypes.h" + +namespace dev +{ +namespace eth +{ +namespace jit +{ + class ExecutionEngine; +} +} + +namespace evmjit +{ + +class JIT +{ +public: + + /// Ask JIT if the EVM code is ready for execution. + /// Returns `true` if the EVM code has been compiled and loaded into memory. + /// In this case the code can be executed without overhead. + /// \param _codeHash The Keccak hash of the EVM code. + static bool isCodeReady(h256 _codeHash); + +private: + friend class dev::eth::jit::ExecutionEngine; + + static void* getCode(h256 _codeHash); + static void mapCode(h256 _codeHash, void* _funcAddr); +}; + +} +} diff --git a/libevmjit-cpp/CMakeLists.txt b/libevmjit-cpp/CMakeLists.txt index 5b4dbb9c8..5b2a35b00 100644 --- a/libevmjit-cpp/CMakeLists.txt +++ b/libevmjit-cpp/CMakeLists.txt @@ -19,6 +19,7 @@ add_library(${TARGET_NAME} STATIC ${SOURCES}) set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") include_directories(../..) +include_directories(${EVMJIT_INCLUDE_DIR}) include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS}) diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index 2c37412fc..a5a60f48c 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "Utils.h" @@ -16,7 +17,7 @@ extern "C" using namespace dev; using namespace dev::eth; - using jit::i256; + using evmjit::i256; EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) { diff --git a/libevmjit-cpp/Utils.h b/libevmjit-cpp/Utils.h index ac796920b..f9b9b2ef4 100644 --- a/libevmjit-cpp/Utils.h +++ b/libevmjit-cpp/Utils.h @@ -1,13 +1,13 @@ #pragma once -#include +#include namespace dev { namespace eth { -inline u256 llvm2eth(jit::i256 _i) +inline u256 llvm2eth(evmjit::i256 _i) { u256 u = 0; u |= _i.d; @@ -20,9 +20,9 @@ inline u256 llvm2eth(jit::i256 _i) return u; } -inline jit::i256 eth2llvm(u256 _u) +inline evmjit::i256 eth2llvm(u256 _u) { - jit::i256 i; + evmjit::i256 i; u256 mask = 0xFFFFFFFFFFFFFFFF; i.a = static_cast(_u & mask); _u >>= 64; @@ -34,5 +34,11 @@ inline jit::i256 eth2llvm(u256 _u) return i; } +inline evmjit::h256 eth2llvm(h256 _u) +{ + /// Just directly copies memory + return *(evmjit::h256*)&_u; +} + } } diff --git a/libevmjit/CMakeLists.txt b/libevmjit/CMakeLists.txt index 7f4e763d7..4b15d52ac 100644 --- a/libevmjit/CMakeLists.txt +++ b/libevmjit/CMakeLists.txt @@ -8,6 +8,7 @@ set(SOURCES Common.h Compiler.cpp Compiler.h CompilerHelper.cpp CompilerHelper.h + ${EVMJIT_INCLUDE_DIR}/evmjit/DataTypes.h Endianness.cpp Endianness.h ExecStats.cpp ExecStats.h ExecutionEngine.cpp ExecutionEngine.h @@ -15,6 +16,7 @@ set(SOURCES GasMeter.cpp GasMeter.h Instruction.cpp Instruction.h interface.cpp interface.h + JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h Memory.cpp Memory.h Optimizer.cpp Optimizer.h Runtime.cpp Runtime.h @@ -79,6 +81,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION} FOLDER "libs") +include_directories(${EVMJIT_INCLUDE_DIR}) include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen) diff --git a/libevmjit/Common.h b/libevmjit/Common.h index 028f0b3c5..b519614fe 100644 --- a/libevmjit/Common.h +++ b/libevmjit/Common.h @@ -31,7 +31,7 @@ enum class ReturnCode // Standard error codes OutOfGas = -1, - StackUnderflow = -2, + StackUnderflow = -2, BadJumpDestination = -3, BadInstruction = -4, Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected @@ -46,16 +46,6 @@ enum class ReturnCode LinkerWorkaround = -299, }; -/// Representation of 256-bit value binary compatible with LLVM i256 -struct i256 -{ - uint64_t a = 0; - uint64_t b = 0; - uint64_t c = 0; - uint64_t d = 0; -}; -static_assert(sizeof(i256) == 32, "Wrong i265 size"); - #define UNTESTED assert(false) } diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index e15dad969..08ca403b5 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -19,6 +19,7 @@ #include #include "preprocessor/llvm_includes_end.h" +#include "evmjit/JIT.h" #include "Runtime.h" #include "Compiler.h" #include "Optimizer.h" @@ -33,6 +34,7 @@ namespace eth { namespace jit { +using evmjit::JIT; namespace { @@ -119,8 +121,6 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) // TODO: Do not pseudo-init the cache every time auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr; - static std::unordered_map funcCache; - static std::unique_ptr ee; if (!ee) { @@ -147,8 +147,9 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module ee->setObjectCache(objectCache); - if (preloadCache) - Cache::preload(*ee, funcCache); + // FIXME: Disabled during API changes + //if (preloadCache) + // Cache::preload(*ee, funcCache); } static StatsCollector statsCollector; @@ -156,11 +157,8 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) auto mainFuncName = codeHash(_data->codeHash); m_runtime.init(_data, _env); - EntryFuncPtr entryFuncPtr = nullptr; - auto it = funcCache.find(mainFuncName); - if (it != funcCache.end()) - entryFuncPtr = (EntryFuncPtr) it->second; - + // TODO: Remove cast + auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(_data->codeHash); if (!entryFuncPtr) { auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr; @@ -183,12 +181,10 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) module.release(); listener->stateChanged(ExecState::CodeGen); entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); + if (!CHECK(entryFuncPtr)) + return ReturnCode::LLVMLinkError; + JIT::mapCode(_data->codeHash, (void*)entryFuncPtr); // FIXME: Remove cast } - if (!CHECK(entryFuncPtr)) - return ReturnCode::LLVMLinkError; - - if (it == funcCache.end()) - funcCache[mainFuncName] = (uint64_t) entryFuncPtr; listener->stateChanged(ExecState::Execution); auto returnCode = entryFuncPtr(&m_runtime); diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp new file mode 100644 index 000000000..9774c7396 --- /dev/null +++ b/libevmjit/JIT.cpp @@ -0,0 +1,46 @@ +#include "evmjit/JIT.h" + +#include + +namespace dev +{ +namespace evmjit +{ +namespace +{ + +class JITImpl: JIT +{ +public: + std::unordered_map codeMap; + + static JITImpl& instance() + { + static JITImpl s_instance; + return s_instance; + } +}; + +} // anonymous namespace + +bool JIT::isCodeReady(h256 _codeHash) +{ + return JITImpl::instance().codeMap.count(_codeHash) != 0; +} + +void* JIT::getCode(h256 _codeHash) +{ + auto& codeMap = JITImpl::instance().codeMap; + auto it = codeMap.find(_codeHash); + if (it != codeMap.end()) + return it->second; + return nullptr; +} + +void JIT::mapCode(h256 _codeHash, void* _funcAddr) +{ + JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr)); +} + +} +} diff --git a/libevmjit/RuntimeData.h b/libevmjit/RuntimeData.h index cc081cc58..6a5cd0d14 100644 --- a/libevmjit/RuntimeData.h +++ b/libevmjit/RuntimeData.h @@ -1,5 +1,6 @@ #pragma once +#include "evmjit/DataTypes.h" #include "Common.h" namespace dev @@ -8,7 +9,9 @@ namespace eth { namespace jit { - +using evmjit::i256; +using evmjit::h256; + struct RuntimeData { enum Index @@ -49,7 +52,7 @@ struct RuntimeData int64_t timestamp = 0; byte const* code = nullptr; uint64_t codeSize = 0; - i256 codeHash; + h256 codeHash; }; /// VM Environment (ExtVM) opaque type From 9c97f20674e73128541902dff1a444067a9c1abb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 6 May 2015 10:29:36 +0200 Subject: [PATCH 12/72] Allow selecting VM kind manually --- libevmjit-cpp/JitVM.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 84cc41dd1..7acbec5c1 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -32,9 +32,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) if (rejected) { cwarn << "Execution rejected by EVM JIT (gas limit: " << m_gas << "), executing with interpreter"; - VMFactory::setKind(VMKind::Interpreter); - m_fallbackVM = VMFactory::create(m_gas); - VMFactory::setKind(VMKind::JIT); + m_fallbackVM = VMFactory::create(VMKind::Interpreter, m_gas); auto&& output = m_fallbackVM->go(_ext, _onOp, _step); m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it return output; From 854d088da9d11abf3e258f0d1a525487873f6d8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 7 May 2015 11:28:01 +0200 Subject: [PATCH 13/72] Unpack runtime data at front. Not used one are removed by DCE pass. --- libevmjit/Optimizer.cpp | 1 + libevmjit/RuntimeManager.cpp | 46 +++++++++++++++++++----------------- libevmjit/RuntimeManager.h | 2 ++ 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index 4913dcaa0..23550a7ea 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -79,6 +79,7 @@ bool LowerEVMPass::doFinalization(llvm::Module&) bool prepare(llvm::Module& _module) { auto pm = llvm::legacy::PassManager{}; + pm.add(llvm::createDeadCodeEliminationPass()); pm.add(new LowerEVMPass{}); return pm.run(_module); } diff --git a/libevmjit/RuntimeManager.cpp b/libevmjit/RuntimeManager.cpp index 8d000f4c8..154aa0fd0 100644 --- a/libevmjit/RuntimeManager.cpp +++ b/libevmjit/RuntimeManager.cpp @@ -64,22 +64,22 @@ llvm::Twine getName(RuntimeData::Index _index) { switch (_index) { - default: return "data"; - case RuntimeData::Address: return "address"; - case RuntimeData::Caller: return "caller"; - case RuntimeData::Origin: return "origin"; - case RuntimeData::CallValue: return "callvalue"; - case RuntimeData::GasPrice: return "gasprice"; - case RuntimeData::CoinBase: return "coinbase"; - case RuntimeData::Difficulty: return "difficulty"; - case RuntimeData::GasLimit: return "gaslimit"; - case RuntimeData::CallData: return "callData"; - case RuntimeData::Code: return "code"; - case RuntimeData::CodeSize: return "code"; - case RuntimeData::CallDataSize: return "callDataSize"; - case RuntimeData::Gas: return "gas"; - case RuntimeData::Number: return "number"; - case RuntimeData::Timestamp: return "timestamp"; + default: return ""; + case RuntimeData::Gas: return "msg.gas"; + case RuntimeData::GasPrice: return "tx.gasprice"; + case RuntimeData::CallData: return "msg.data.ptr"; + case RuntimeData::CallDataSize: return "msg.data.size"; + case RuntimeData::Address: return "this.address"; + case RuntimeData::Caller: return "msg.caller"; + case RuntimeData::Origin: return "tx.origin"; + case RuntimeData::CallValue: return "msg.value"; + case RuntimeData::CoinBase: return "block.coinbase"; + case RuntimeData::Difficulty: return "block.difficulty"; + case RuntimeData::GasLimit: return "block.gaslimit"; + case RuntimeData::Number: return "block.number"; + case RuntimeData::Timestamp: return "block.timestamp"; + case RuntimeData::Code: return "code.ptr"; + case RuntimeData::CodeSize: return "code.size"; } } } @@ -93,9 +93,9 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB // Unpack data auto rtPtr = getRuntimePtr(); - m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data"); + m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr"); assert(m_dataPtr->getType() == Type::RuntimeDataPtr); - m_gasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), m_dataPtr, 0, "gas"); + m_gasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), m_dataPtr, 0, "gasPtr"); assert(m_gasPtr->getType() == Type::Gas->getPointerTo()); m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem"); assert(m_memPtr->getType() == Array::getType()->getPointerTo()); @@ -105,6 +105,10 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize"); m_builder.CreateStore(m_builder.getInt64(0), m_stackSize); + auto data = m_builder.CreateLoad(m_dataPtr, "data"); + for (unsigned i = 0; i < m_dataElts.size(); ++i) + m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i))); + llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr}; m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule()); m_checkStackLimit->setDoesNotThrow(); @@ -180,7 +184,7 @@ llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) llvm::Value* RuntimeManager::get(RuntimeData::Index _index) { - return getBuilder().CreateLoad(getPtr(_index), getName(_index)); + return m_dataElts[_index]; } void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value) @@ -264,9 +268,7 @@ llvm::Value* RuntimeManager::getCallDataSize() llvm::Value* RuntimeManager::getGas() { - auto gas = get(RuntimeData::Gas); - assert(gas->getType() == Type::Gas); - return gas; + return getBuilder().CreateLoad(getGasPtr(), "gas"); } llvm::Value* RuntimeManager::getGasPtr() diff --git a/libevmjit/RuntimeManager.h b/libevmjit/RuntimeManager.h index c430a1098..4c36c4ef3 100644 --- a/libevmjit/RuntimeManager.h +++ b/libevmjit/RuntimeManager.h @@ -61,6 +61,8 @@ private: llvm::Value* m_memPtr = nullptr; llvm::Value* m_envPtr = nullptr; + std::array(RuntimeData::Index::CodeSize) + 1> m_dataElts; + llvm::Value* m_stackSize = nullptr; llvm::Function* m_checkStackLimit = nullptr; From 9496f645e24ecc78e3c71a476cb212edbed15777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 7 May 2015 16:20:04 +0200 Subject: [PATCH 14/72] Always exit through exit blocks (Stop, Abort, etc). Fixes ethereum/evmjit#6. --- libevmjit/Compiler.cpp | 10 +++++----- libevmjit/Compiler.h | 3 +++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index a2f58c770..038d9ef3f 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -159,10 +159,10 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera // TODO: Create Stop basic block on demand m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); - auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc); + m_abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc); auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm(); - m_builder.CreateCondBr(normalFlow, firstBB, abortBB, Type::expectTrue); + m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue); for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt) { @@ -178,7 +178,7 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera m_builder.SetInsertPoint(m_stopBB); runtimeManager.exit(ReturnCode::Stop); - m_builder.SetInsertPoint(abortBB); + m_builder.SetInsertPoint(m_abortBB); runtimeManager.exit(ReturnCode::OutOfGas); removeDeadBlocks(); @@ -789,7 +789,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti case Instruction::STOP: { - m_builder.CreateRet(Constant::get(ReturnCode::Stop)); + m_builder.CreateBr(m_stopBB); break; } @@ -816,7 +816,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti } default: // Invalid instruction - abort - m_builder.CreateRet(Constant::get(ReturnCode::BadInstruction)); + m_builder.CreateBr(m_abortBB); it = _basicBlock.end() - 1; // finish block compilation } } diff --git a/libevmjit/Compiler.h b/libevmjit/Compiler.h index 4469389bb..9b9fe7160 100644 --- a/libevmjit/Compiler.h +++ b/libevmjit/Compiler.h @@ -65,6 +65,9 @@ private: /// Stop basic block - terminates execution with STOP code (0) llvm::BasicBlock* m_stopBB = nullptr; + /// Abort basic block - terminates execution with OOG-like state + llvm::BasicBlock* m_abortBB = nullptr; + /// Block with a jump table. std::unique_ptr m_jumpTableBlock; From 624677eb42e0af80430678de9b3bc956772d9ab2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 7 May 2015 16:34:44 +0200 Subject: [PATCH 15/72] Copy gas counter to local function stack (alloca) --- libevmjit/RuntimeManager.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/libevmjit/RuntimeManager.cpp b/libevmjit/RuntimeManager.cpp index 154aa0fd0..d48b64bb1 100644 --- a/libevmjit/RuntimeManager.cpp +++ b/libevmjit/RuntimeManager.cpp @@ -95,8 +95,6 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB auto rtPtr = getRuntimePtr(); m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr"); assert(m_dataPtr->getType() == Type::RuntimeDataPtr); - m_gasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), m_dataPtr, 0, "gasPtr"); - assert(m_gasPtr->getType() == Type::Gas->getPointerTo()); m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem"); assert(m_memPtr->getType() == Array::getType()->getPointerTo()); m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env"); @@ -109,6 +107,9 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB for (unsigned i = 0; i < m_dataElts.size(); ++i) m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i))); + m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr"); + m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr); + llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr}; m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule()); m_checkStackLimit->setDoesNotThrow(); @@ -215,6 +216,8 @@ void RuntimeManager::exit(ReturnCode _returnCode) if (m_stack) m_stack->free(); + auto extGasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), RuntimeData::Index::Gas, "msg.gas.ptr"); + m_builder.CreateStore(getGas(), extGasPtr); m_builder.CreateRet(Constant::get(_returnCode)); } @@ -286,7 +289,7 @@ llvm::Value* RuntimeManager::getMem() void RuntimeManager::setGas(llvm::Value* _gas) { assert(_gas->getType() == Type::Gas); - set(RuntimeData::Gas, _gas); + getBuilder().CreateStore(_gas, getGasPtr()); } } From dd3f1fe2540c4905b5581d32ee7e264a1c0d1207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 11 May 2015 14:07:27 +0200 Subject: [PATCH 16/72] Remove gas counter from VM interface (VMFace) --- libevmjit-cpp/JitVM.cpp | 16 +++++++--------- libevmjit-cpp/JitVM.h | 3 +-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 84cc41dd1..717d70958 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -18,29 +18,27 @@ namespace eth extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below -bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) +bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) { using namespace jit; auto rejected = false; // TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope - rejected |= m_gas > std::numeric_limits::max(); // Do not accept requests with gas > 2^63 (int64 max) + rejected |= io_gas > std::numeric_limits::max(); // Do not accept requests with gas > 2^63 (int64 max) rejected |= _ext.gasPrice > std::numeric_limits::max(); rejected |= _ext.currentBlock.number > std::numeric_limits::max(); rejected |= _ext.currentBlock.timestamp > std::numeric_limits::max(); if (rejected) { - cwarn << "Execution rejected by EVM JIT (gas limit: " << m_gas << "), executing with interpreter"; + cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter"; VMFactory::setKind(VMKind::Interpreter); - m_fallbackVM = VMFactory::create(m_gas); + m_fallbackVM = VMFactory::create(); VMFactory::setKind(VMKind::JIT); - auto&& output = m_fallbackVM->go(_ext, _onOp, _step); - m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it - return output; + return m_fallbackVM->go(io_gas, _ext, _onOp, _step); } - m_data.gas = static_cast(m_gas); + m_data.gas = static_cast(io_gas); m_data.gasPrice = static_cast(_ext.gasPrice); m_data.callData = _ext.data.data(); m_data.callDataSize = _ext.data.size(); @@ -80,7 +78,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) break; } - m_gas = m_data.gas; // TODO: Remove m_gas field + io_gas = m_data.gas; return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)}; } diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 58caa3648..797fe7e1c 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -10,11 +10,10 @@ namespace eth class JitVM: public VMFace { - virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; private: friend class VMFactory; - explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} jit::RuntimeData m_data; jit::ExecutionEngine m_engine; From d92198969655a7a156f1008a6a7761f4dcd4778d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 12 May 2015 14:29:06 +0200 Subject: [PATCH 17/72] VM cleanups --- libevmjit-cpp/JitVM.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 797fe7e1c..e6864f885 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -10,11 +10,10 @@ namespace eth class JitVM: public VMFace { +public: virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; private: - friend class VMFactory; - jit::RuntimeData m_data; jit::ExecutionEngine m_engine; std::unique_ptr m_fallbackVM; ///< VM used in case of input data rejected by JIT From f72bfb461eee826e2a9619565272e814d5fea6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 13 May 2015 12:39:55 +0200 Subject: [PATCH 18/72] Lower DIV to a function call in the LLVM pass after optimization. --- libevmjit/Arith256.cpp | 114 ++++++++++++++++++++++++++++++++++++++++ libevmjit/Arith256.h | 2 + libevmjit/Compiler.cpp | 4 +- libevmjit/Optimizer.cpp | 10 ++++ 4 files changed, 128 insertions(+), 2 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index 107eb5b05..caef06bec 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -86,6 +86,120 @@ llvm::Function* Arith256::getMulFunc(llvm::Module& _module) return func; } +llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module) +{ + static const auto funcName = "evm.udivrem.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + auto type = Type::Word; + + // Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research + // The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder + + llvm::Type* argTypes[] = {type, type}; + auto retType = llvm::VectorType::get(type, 2); + auto func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto zero = llvm::ConstantInt::get(type, 0); + auto one = llvm::ConstantInt::get(type, 1); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto yArg = x->getNextNode(); + yArg->setName("y"); + + auto entryBB = llvm::BasicBlock::Create(_module.getContext(), "Entry", func); + auto mainBB = llvm::BasicBlock::Create(_module.getContext(), "Main", func); + auto loopBB = llvm::BasicBlock::Create(_module.getContext(), "Loop", func); + auto continueBB = llvm::BasicBlock::Create(_module.getContext(), "Continue", func); + auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func); + + auto builder = llvm::IRBuilder<>{entryBB}; + auto yNonZero = builder.CreateICmpNE(yArg, zero); + auto yLEx = builder.CreateICmpULE(yArg, x); + auto r0 = builder.CreateSelect(yNonZero, x, zero, "r0"); + builder.CreateCondBr(builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB); + + builder.SetInsertPoint(mainBB); + auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, type); + // both y and r are non-zero + auto yLz = builder.CreateCall2(ctlzIntr, yArg, builder.getInt1(true), "y.lz"); + auto rLz = builder.CreateCall2(ctlzIntr, r0, builder.getInt1(true), "r.lz"); + auto i0 = builder.CreateNUWSub(yLz, rLz, "i0"); + auto y0 = builder.CreateShl(yArg, i0); + builder.CreateBr(loopBB); + + builder.SetInsertPoint(loopBB); + auto yPhi = builder.CreatePHI(type, 2, "y.phi"); + auto rPhi = builder.CreatePHI(type, 2, "r.phi"); + auto iPhi = builder.CreatePHI(type, 2, "i.phi"); + auto qPhi = builder.CreatePHI(type, 2, "q.phi"); + auto rUpdate = builder.CreateNUWSub(rPhi, yPhi); + auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0 + auto rGEy = builder.CreateICmpUGE(rPhi, yPhi); + auto r1 = builder.CreateSelect(rGEy, rUpdate, rPhi, "r1"); + auto q1 = builder.CreateSelect(rGEy, qUpdate, qPhi, "q"); + auto iZero = builder.CreateICmpEQ(iPhi, zero); + builder.CreateCondBr(iZero, returnBB, continueBB); + + builder.SetInsertPoint(continueBB); + auto i2 = builder.CreateNUWSub(iPhi, one); + auto q2 = builder.CreateShl(q1, one); + auto y2 = builder.CreateLShr(yPhi, one); + builder.CreateBr(loopBB); + + yPhi->addIncoming(y0, mainBB); + yPhi->addIncoming(y2, continueBB); + rPhi->addIncoming(r0, mainBB); + rPhi->addIncoming(r1, continueBB); + iPhi->addIncoming(i0, mainBB); + iPhi->addIncoming(i2, continueBB); + qPhi->addIncoming(zero, mainBB); + qPhi->addIncoming(q2, continueBB); + + builder.SetInsertPoint(returnBB); + auto qRet = builder.CreatePHI(type, 2, "q.ret"); + qRet->addIncoming(zero, entryBB); + qRet->addIncoming(q1, loopBB); + auto rRet = builder.CreatePHI(type, 2, "r.ret"); + rRet->addIncoming(r0, entryBB); + rRet->addIncoming(r1, loopBB); + auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0"); + ret = builder.CreateInsertElement(ret, rRet, 1, "ret"); + builder.CreateRet(ret); + + return func; +} + +llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module) +{ + static const auto funcName = "evm.udiv.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + auto udivremFunc = getUDivRem256Func(_module); + + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); + auto builder = llvm::IRBuilder<>{bb}; + auto udivrem = builder.CreateCall(udivremFunc, {x, y}); + auto udiv = builder.CreateExtractElement(udivrem, uint64_t(0)); + builder.CreateRet(udiv); + + return func; +} + llvm::Function* Arith256::getMul512Func() { auto& func = m_mul512; diff --git a/libevmjit/Arith256.h b/libevmjit/Arith256.h index a5762429d..3ee016073 100644 --- a/libevmjit/Arith256.h +++ b/libevmjit/Arith256.h @@ -23,6 +23,8 @@ public: void debug(llvm::Value* _value, char _c); static llvm::Function* getMulFunc(llvm::Module& _module); + static llvm::Function* getUDiv256Func(llvm::Module& _module); + static llvm::Function* getUDivRem256Func(llvm::Module& _module); private: llvm::Function* getMul512Func(); diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 038d9ef3f..2e4976c78 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -279,8 +279,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti { auto lhs = stack.pop(); auto rhs = stack.pop(); - auto res = _arith.div(lhs, rhs); - stack.push(res.first); + auto res = m_builder.CreateUDiv(lhs, rhs); + stack.push(res); break; } diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index 23550a7ea..7bd7f252c 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -65,6 +65,15 @@ bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb) modified = true; } } + else if (inst.getOpcode() == llvm::Instruction::UDiv) + { + if (inst.getType() == Type::Word) + { + auto call = llvm::CallInst::Create(Arith256::getUDiv256Func(*module), {inst.getOperand(0), inst.getOperand(1)}, "", &inst); + inst.replaceAllUsesWith(call); + modified = true; + } + } } return modified; } @@ -81,6 +90,7 @@ bool prepare(llvm::Module& _module) auto pm = llvm::legacy::PassManager{}; pm.add(llvm::createDeadCodeEliminationPass()); pm.add(new LowerEVMPass{}); + pm.add(llvm::createDeadInstEliminationPass()); // Remove leftovers return pm.run(_module); } From f7a4e27d46c7dd675d6808d7e68f023e4a857bb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 13 May 2015 13:10:22 +0200 Subject: [PATCH 19/72] Eliminate dead instructions replaced in AP arithmetic lowering. --- libevmjit/Optimizer.cpp | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index 7bd7f252c..982f1cefe 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -54,25 +54,30 @@ bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb) { auto modified = false; auto module = _bb.getParent()->getParent(); - for (auto&& inst : _bb) + for (auto it = _bb.begin(); it != _bb.end(); ) { - if (inst.getOpcode() == llvm::Instruction::Mul) + auto& inst = *it++; + llvm::Function* func = nullptr; + if (inst.getType() == Type::Word) { - if (inst.getType() == Type::Word) + switch (inst.getOpcode()) { - auto call = llvm::CallInst::Create(Arith256::getMulFunc(*module), {inst.getOperand(0), inst.getOperand(1)}, "", &inst); - inst.replaceAllUsesWith(call); - modified = true; + case llvm::Instruction::Mul: + func = Arith256::getMulFunc(*module); + break; + + case llvm::Instruction::UDiv: + func = Arith256::getUDiv256Func(*module); + break; } } - else if (inst.getOpcode() == llvm::Instruction::UDiv) + + if (func) { - if (inst.getType() == Type::Word) - { - auto call = llvm::CallInst::Create(Arith256::getUDiv256Func(*module), {inst.getOperand(0), inst.getOperand(1)}, "", &inst); - inst.replaceAllUsesWith(call); - modified = true; - } + auto call = llvm::CallInst::Create(func, {inst.getOperand(0), inst.getOperand(1)}, "", &inst); + inst.replaceAllUsesWith(call); + inst.eraseFromParent(); + modified = true; } } return modified; @@ -90,7 +95,6 @@ bool prepare(llvm::Module& _module) auto pm = llvm::legacy::PassManager{}; pm.add(llvm::createDeadCodeEliminationPass()); pm.add(new LowerEVMPass{}); - pm.add(llvm::createDeadInstEliminationPass()); // Remove leftovers return pm.run(_module); } From 2603d3afbe6b2eaef00d1bd8764a966adebfb1e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 13 May 2015 14:07:49 +0200 Subject: [PATCH 20/72] Lower MOD, SDIV & SMOD to a function call in the LLVM pass after optimization. --- libevmjit/Arith256.cpp | 180 ++++++++++++++++++++++++++++------------ libevmjit/Arith256.h | 6 +- libevmjit/Compiler.cpp | 12 +-- libevmjit/Optimizer.cpp | 12 +++ 4 files changed, 147 insertions(+), 63 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index caef06bec..48e3307df 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -200,6 +200,131 @@ llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module) return func; } +llvm::Function* Arith256::getURem256Func(llvm::Module& _module) +{ + static const auto funcName = "evm.urem.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + auto udivremFunc = getUDivRem256Func(_module); + + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); + auto builder = llvm::IRBuilder<>{bb}; + auto udivrem = builder.CreateCall(udivremFunc, {x, y}); + auto r = builder.CreateExtractElement(udivrem, uint64_t(1)); + builder.CreateRet(r); + + return func; +} + +llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module) +{ + static const auto funcName = "evm.sdivrem.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + auto udivremFunc = getUDivRem256Func(_module); + + auto retType = llvm::VectorType::get(Type::Word, 2); + auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), "", func); + auto builder = llvm::IRBuilder<>{bb}; + auto xIsNeg = builder.CreateICmpSLT(x, Constant::get(0)); + auto xNeg = builder.CreateSub(Constant::get(0), x); + auto xAbs = builder.CreateSelect(xIsNeg, xNeg, x); + + auto yIsNeg = builder.CreateICmpSLT(y, Constant::get(0)); + auto yNeg = builder.CreateSub(Constant::get(0), y); + auto yAbs = builder.CreateSelect(yIsNeg, yNeg, y); + + auto res = builder.CreateCall(udivremFunc, {xAbs, yAbs}); + auto qAbs = builder.CreateExtractElement(res, uint64_t(0)); + auto rAbs = builder.CreateExtractElement(res, 1); + + // the reminder has the same sign as dividend + auto rNeg = builder.CreateSub(Constant::get(0), rAbs); + auto r = builder.CreateSelect(xIsNeg, rNeg, rAbs); + + auto qNeg = builder.CreateSub(Constant::get(0), qAbs); + auto xyOpposite = builder.CreateXor(xIsNeg, yIsNeg); + auto q = builder.CreateSelect(xyOpposite, qNeg, qAbs); + + auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), q, uint64_t(0)); + ret = builder.CreateInsertElement(ret, r, 1); + builder.CreateRet(ret); + + return func; +} + +llvm::Function* Arith256::getSDiv256Func(llvm::Module& _module) +{ + static const auto funcName = "evm.sdiv.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + auto sdivremFunc = getSDivRem256Func(_module); + + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); + auto builder = llvm::IRBuilder<>{bb}; + auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); + auto q = builder.CreateExtractElement(sdivrem, uint64_t(0)); + builder.CreateRet(q); + + return func; +} + +llvm::Function* Arith256::getSRem256Func(llvm::Module& _module) +{ + static const auto funcName = "evm.srem.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + auto sdivremFunc = getSDivRem256Func(_module); + + auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); + auto builder = llvm::IRBuilder<>{bb}; + auto sdivrem = builder.CreateCall(sdivremFunc, {x, y}); + auto r = builder.CreateExtractElement(sdivrem, uint64_t(1)); + builder.CreateRet(r); + + return func; +} + llvm::Function* Arith256::getMul512Func() { auto& func = m_mul512; @@ -462,61 +587,6 @@ llvm::Function* Arith256::getMulModFunc() return m_mulmod; } -std::pair Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2) -{ - if (auto c1 = llvm::dyn_cast(_arg1)) - { - if (auto c2 = llvm::dyn_cast(_arg2)) - { - if (!c2->getValue()) - return std::make_pair(Constant::get(0), Constant::get(0)); - auto div = Constant::get(c1->getValue().udiv(c2->getValue())); - auto mod = Constant::get(c1->getValue().urem(c2->getValue())); - return std::make_pair(div, mod); - } - } - - auto r = createCall(getDivFunc(Type::Word), {_arg1, _arg2}); - auto div = m_builder.CreateExtractElement(r, uint64_t(0), "div"); - auto mod = m_builder.CreateExtractElement(r, 1, "mod"); - return std::make_pair(div, mod); -} - -std::pair Arith256::sdiv(llvm::Value* _x, llvm::Value* _y) -{ - if (auto c1 = llvm::dyn_cast(_x)) - { - if (auto c2 = llvm::dyn_cast(_y)) - { - if (!c2->getValue()) - return std::make_pair(Constant::get(0), Constant::get(0)); - auto div = Constant::get(c1->getValue().sdiv(c2->getValue())); - auto mod = Constant::get(c1->getValue().srem(c2->getValue())); - return std::make_pair(div, mod); - } - } - - auto xIsNeg = m_builder.CreateICmpSLT(_x, Constant::get(0)); - auto xNeg = m_builder.CreateSub(Constant::get(0), _x); - auto xAbs = m_builder.CreateSelect(xIsNeg, xNeg, _x); - - auto yIsNeg = m_builder.CreateICmpSLT(_y, Constant::get(0)); - auto yNeg = m_builder.CreateSub(Constant::get(0), _y); - auto yAbs = m_builder.CreateSelect(yIsNeg, yNeg, _y); - - auto res = div(xAbs, yAbs); - - // the reminder has the same sign as dividend - auto rNeg = m_builder.CreateSub(Constant::get(0), res.second); - res.second = m_builder.CreateSelect(xIsNeg, rNeg, res.second); - - auto qNeg = m_builder.CreateSub(Constant::get(0), res.first); - auto xyOpposite = m_builder.CreateXor(xIsNeg, yIsNeg); - res.first = m_builder.CreateSelect(xyOpposite, qNeg, res.first); - - return res; -} - llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) { // while (e != 0) { diff --git a/libevmjit/Arith256.h b/libevmjit/Arith256.h index 3ee016073..aeea830db 100644 --- a/libevmjit/Arith256.h +++ b/libevmjit/Arith256.h @@ -14,8 +14,6 @@ class Arith256 : public CompilerHelper public: Arith256(llvm::IRBuilder<>& _builder); - std::pair div(llvm::Value* _arg1, llvm::Value* _arg2); - std::pair sdiv(llvm::Value* _arg1, llvm::Value* _arg2); llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); llvm::Value* mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); llvm::Value* addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); @@ -24,7 +22,11 @@ public: static llvm::Function* getMulFunc(llvm::Module& _module); static llvm::Function* getUDiv256Func(llvm::Module& _module); + static llvm::Function* getURem256Func(llvm::Module& _module); static llvm::Function* getUDivRem256Func(llvm::Module& _module); + static llvm::Function* getSDiv256Func(llvm::Module& _module); + static llvm::Function* getSRem256Func(llvm::Module& _module); + static llvm::Function* getSDivRem256Func(llvm::Module& _module); private: llvm::Function* getMul512Func(); diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 2e4976c78..7ae3e067b 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -288,8 +288,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti { auto lhs = stack.pop(); auto rhs = stack.pop(); - auto res = _arith.sdiv(lhs, rhs); - stack.push(res.first); + auto res = m_builder.CreateSDiv(lhs, rhs); + stack.push(res); break; } @@ -297,8 +297,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti { auto lhs = stack.pop(); auto rhs = stack.pop(); - auto res = _arith.div(lhs, rhs); - stack.push(res.second); + auto res = m_builder.CreateURem(lhs, rhs); + stack.push(res); break; } @@ -306,8 +306,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti { auto lhs = stack.pop(); auto rhs = stack.pop(); - auto res = _arith.sdiv(lhs, rhs); - stack.push(res.second); + auto res = m_builder.CreateSRem(lhs, rhs); + stack.push(res); break; } diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index 982f1cefe..8eaab9a99 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -69,6 +69,18 @@ bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb) case llvm::Instruction::UDiv: func = Arith256::getUDiv256Func(*module); break; + + case llvm::Instruction::URem: + func = Arith256::getURem256Func(*module); + break; + + case llvm::Instruction::SDiv: + func = Arith256::getSDiv256Func(*module); + break; + + case llvm::Instruction::SRem: + func = Arith256::getSRem256Func(*module); + break; } } From f1428f804ada8e332b7070acdf187042be77257a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 13 May 2015 17:21:13 +0200 Subject: [PATCH 21/72] Add edge DIV/MOD cases. Migrate to new udivrem function. --- libevmjit/Arith256.cpp | 176 +++++++++++++---------------------------- libevmjit/Arith256.h | 5 +- libevmjit/Compiler.cpp | 43 ++++++---- 3 files changed, 86 insertions(+), 138 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index 48e3307df..64ed7e649 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -86,25 +86,20 @@ llvm::Function* Arith256::getMulFunc(llvm::Module& _module) return func; } -llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module) +namespace +{ +llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) { - static const auto funcName = "evm.udivrem.i256"; - if (auto func = _module.getFunction(funcName)) - return func; - - auto type = Type::Word; - // Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research // The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder - llvm::Type* argTypes[] = {type, type}; - auto retType = llvm::VectorType::get(type, 2); - auto func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, &_module); + auto retType = llvm::VectorType::get(_type, 2); + auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); func->setDoesNotThrow(); func->setDoesNotAccessMemory(); - auto zero = llvm::ConstantInt::get(type, 0); - auto one = llvm::ConstantInt::get(type, 1); + auto zero = llvm::ConstantInt::get(_type, 0); + auto one = llvm::ConstantInt::get(_type, 1); auto x = &func->getArgumentList().front(); x->setName("x"); @@ -124,7 +119,7 @@ llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module) builder.CreateCondBr(builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB); builder.SetInsertPoint(mainBB); - auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, type); + auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type); // both y and r are non-zero auto yLz = builder.CreateCall2(ctlzIntr, yArg, builder.getInt1(true), "y.lz"); auto rLz = builder.CreateCall2(ctlzIntr, r0, builder.getInt1(true), "r.lz"); @@ -133,10 +128,10 @@ llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module) builder.CreateBr(loopBB); builder.SetInsertPoint(loopBB); - auto yPhi = builder.CreatePHI(type, 2, "y.phi"); - auto rPhi = builder.CreatePHI(type, 2, "r.phi"); - auto iPhi = builder.CreatePHI(type, 2, "i.phi"); - auto qPhi = builder.CreatePHI(type, 2, "q.phi"); + auto yPhi = builder.CreatePHI(_type, 2, "y.phi"); + auto rPhi = builder.CreatePHI(_type, 2, "r.phi"); + auto iPhi = builder.CreatePHI(_type, 2, "i.phi"); + auto qPhi = builder.CreatePHI(_type, 2, "q.phi"); auto rUpdate = builder.CreateNUWSub(rPhi, yPhi); auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0 auto rGEy = builder.CreateICmpUGE(rPhi, yPhi); @@ -161,10 +156,10 @@ llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module) qPhi->addIncoming(q2, continueBB); builder.SetInsertPoint(returnBB); - auto qRet = builder.CreatePHI(type, 2, "q.ret"); + auto qRet = builder.CreatePHI(_type, 2, "q.ret"); qRet->addIncoming(zero, entryBB); qRet->addIncoming(q1, loopBB); - auto rRet = builder.CreatePHI(type, 2, "r.ret"); + auto rRet = builder.CreatePHI(_type, 2, "r.ret"); rRet->addIncoming(r0, entryBB); rRet->addIncoming(r1, loopBB); auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0"); @@ -173,6 +168,25 @@ llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module) return func; } +} + +llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module) +{ + static const auto funcName = "evm.udivrem.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + + return createUDivRemFunc(Type::Word, _module, funcName); +} + +llvm::Function* Arith256::getUDivRem512Func(llvm::Module& _module) +{ + static const auto funcName = "evm.udivrem.i512"; + if (auto func = _module.getFunction(funcName)) + return func; + + return createUDivRemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); +} llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module) { @@ -200,15 +214,13 @@ llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module) return func; } -llvm::Function* Arith256::getURem256Func(llvm::Module& _module) +namespace { - static const auto funcName = "evm.urem.i256"; - if (auto func = _module.getFunction(funcName)) - return func; - - auto udivremFunc = getUDivRem256Func(_module); +llvm::Function* createURemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) +{ + auto udivremFunc = _type == Type::Word ? Arith256::getUDivRem256Func(_module) : Arith256::getUDivRem512Func(_module); - auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); + auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); func->setDoesNotThrow(); func->setDoesNotAccessMemory(); @@ -225,6 +237,23 @@ llvm::Function* Arith256::getURem256Func(llvm::Module& _module) return func; } +} + +llvm::Function* Arith256::getURem256Func(llvm::Module& _module) +{ + static const auto funcName = "evm.urem.i256"; + if (auto func = _module.getFunction(funcName)) + return func; + return createURemFunc(Type::Word, _module, funcName); +} + +llvm::Function* Arith256::getURem512Func(llvm::Module& _module) +{ + static const auto funcName = "evm.urem.i512"; + if (auto func = _module.getFunction(funcName)) + return func; + return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName); +} llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module) { @@ -366,95 +395,6 @@ llvm::Function* Arith256::getMul512Func() return func; } -llvm::Function* Arith256::getDivFunc(llvm::Type* _type) -{ - auto& func = _type == Type::Word ? m_div : m_div512; - - if (!func) - { - // Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research - // The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder - - llvm::Type* argTypes[] = {_type, _type}; - auto retType = llvm::VectorType::get(_type, 2); - auto funcName = _type == Type::Word ? "div" : "div512"; - func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule()); - func->setDoesNotThrow(); - func->setDoesNotAccessMemory(); - - auto zero = llvm::ConstantInt::get(_type, 0); - auto one = llvm::ConstantInt::get(_type, 1); - - auto x = &func->getArgumentList().front(); - x->setName("x"); - auto yArg = x->getNextNode(); - yArg->setName("y"); - - InsertPointGuard guard{m_builder}; - - auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func); - auto mainBB = llvm::BasicBlock::Create(m_builder.getContext(), "Main", func); - auto loopBB = llvm::BasicBlock::Create(m_builder.getContext(), "Loop", func); - auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", func); - auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func); - - m_builder.SetInsertPoint(entryBB); - auto yNonZero = m_builder.CreateICmpNE(yArg, zero); - auto yLEx = m_builder.CreateICmpULE(yArg, x); - auto r0 = m_builder.CreateSelect(yNonZero, x, zero, "r0"); - m_builder.CreateCondBr(m_builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB); - - m_builder.SetInsertPoint(mainBB); - auto ctlzIntr = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, _type); - // both y and r are non-zero - auto yLz = m_builder.CreateCall2(ctlzIntr, yArg, m_builder.getInt1(true), "y.lz"); - auto rLz = m_builder.CreateCall2(ctlzIntr, r0, m_builder.getInt1(true), "r.lz"); - auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0"); - auto y0 = m_builder.CreateShl(yArg, i0); - m_builder.CreateBr(loopBB); - - m_builder.SetInsertPoint(loopBB); - auto yPhi = m_builder.CreatePHI(_type, 2, "y.phi"); - auto rPhi = m_builder.CreatePHI(_type, 2, "r.phi"); - auto iPhi = m_builder.CreatePHI(_type, 2, "i.phi"); - auto qPhi = m_builder.CreatePHI(_type, 2, "q.phi"); - auto rUpdate = m_builder.CreateNUWSub(rPhi, yPhi); - auto qUpdate = m_builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0 - auto rGEy = m_builder.CreateICmpUGE(rPhi, yPhi); - auto r1 = m_builder.CreateSelect(rGEy, rUpdate, rPhi, "r1"); - auto q1 = m_builder.CreateSelect(rGEy, qUpdate, qPhi, "q"); - auto iZero = m_builder.CreateICmpEQ(iPhi, zero); - m_builder.CreateCondBr(iZero, returnBB, continueBB); - - m_builder.SetInsertPoint(continueBB); - auto i2 = m_builder.CreateNUWSub(iPhi, one); - auto q2 = m_builder.CreateShl(q1, one); - auto y2 = m_builder.CreateLShr(yPhi, one); - m_builder.CreateBr(loopBB); - - yPhi->addIncoming(y0, mainBB); - yPhi->addIncoming(y2, continueBB); - rPhi->addIncoming(r0, mainBB); - rPhi->addIncoming(r1, continueBB); - iPhi->addIncoming(i0, mainBB); - iPhi->addIncoming(i2, continueBB); - qPhi->addIncoming(zero, mainBB); - qPhi->addIncoming(q2, continueBB); - - m_builder.SetInsertPoint(returnBB); - auto qRet = m_builder.CreatePHI(_type, 2, "q.ret"); - qRet->addIncoming(zero, entryBB); - qRet->addIncoming(q1, loopBB); - auto rRet = m_builder.CreatePHI(_type, 2, "r.ret"); - rRet->addIncoming(r0, entryBB); - rRet->addIncoming(r1, loopBB); - auto ret = m_builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0"); - ret = m_builder.CreateInsertElement(ret, rRet, 1, "ret"); - m_builder.CreateRet(ret); - } - return func; -} - llvm::Function* Arith256::getExpFunc() { if (!m_exp) @@ -550,8 +490,7 @@ llvm::Function* Arith256::getAddModFunc() auto y512 = m_builder.CreateZExt(y, i512Ty, "y512"); auto m512 = m_builder.CreateZExt(mod, i512Ty, "m512"); auto s = m_builder.CreateAdd(x512, y512, "s"); - auto d = createCall(getDivFunc(i512Ty), {s, m512}); - auto r = m_builder.CreateExtractElement(d, 1, "r"); + auto r = createCall(getURem512Func(*getModule()), {s, m512}); m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word)); } return m_addmod; @@ -580,8 +519,7 @@ llvm::Function* Arith256::getMulModFunc() m_builder.SetInsertPoint(entryBB); auto p = createCall(getMul512Func(), {x, y}); auto m = m_builder.CreateZExt(mod, i512Ty, "m"); - auto d = createCall(getDivFunc(i512Ty), {p, m}); - auto r = m_builder.CreateExtractElement(d, 1, "r"); + auto r = createCall(getURem512Func(*getModule()), {p, m}); m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word)); } return m_mulmod; diff --git a/libevmjit/Arith256.h b/libevmjit/Arith256.h index aeea830db..d6096a4c2 100644 --- a/libevmjit/Arith256.h +++ b/libevmjit/Arith256.h @@ -23,21 +23,20 @@ public: static llvm::Function* getMulFunc(llvm::Module& _module); static llvm::Function* getUDiv256Func(llvm::Module& _module); static llvm::Function* getURem256Func(llvm::Module& _module); + static llvm::Function* getURem512Func(llvm::Module& _module); static llvm::Function* getUDivRem256Func(llvm::Module& _module); static llvm::Function* getSDiv256Func(llvm::Module& _module); static llvm::Function* getSRem256Func(llvm::Module& _module); static llvm::Function* getSDivRem256Func(llvm::Module& _module); + static llvm::Function* getUDivRem512Func(llvm::Module& _module); private: llvm::Function* getMul512Func(); - llvm::Function* getDivFunc(llvm::Type* _type); llvm::Function* getExpFunc(); llvm::Function* getAddModFunc(); llvm::Function* getMulModFunc(); llvm::Function* m_mul512 = nullptr; - llvm::Function* m_div = nullptr; - llvm::Function* m_div512 = nullptr; llvm::Function* m_exp = nullptr; llvm::Function* m_addmod = nullptr; llvm::Function* m_mulmod = nullptr; diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 7ae3e067b..33d802f90 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -277,37 +277,48 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti case Instruction::DIV: { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = m_builder.CreateUDiv(lhs, rhs); - stack.push(res); + auto d = stack.pop(); + auto n = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + auto r = m_builder.CreateUDiv(d, n); + r = m_builder.CreateSelect(divByZero, Constant::get(0), r); + stack.push(r); break; } case Instruction::SDIV: { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = m_builder.CreateSDiv(lhs, rhs); - stack.push(res); + auto d = stack.pop(); + auto n = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1)); + auto r = m_builder.CreateSDiv(d, n); + r = m_builder.CreateSelect(divByZero, Constant::get(0), r); + auto dNeg = m_builder.CreateSub(Constant::get(0), d); + r = m_builder.CreateSelect(divByMinusOne, dNeg, r); + stack.push(r); break; } case Instruction::MOD: { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = m_builder.CreateURem(lhs, rhs); - stack.push(res); + auto d = stack.pop(); + auto n = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + auto r = m_builder.CreateURem(d, n); + r = m_builder.CreateSelect(divByZero, Constant::get(0), r); + stack.push(r); break; } case Instruction::SMOD: { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = m_builder.CreateSRem(lhs, rhs); - stack.push(res); + auto d = stack.pop(); + auto n = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + auto r = m_builder.CreateSRem(d, n); + r = m_builder.CreateSelect(divByZero, Constant::get(0), r); + stack.push(r); break; } From d8da43e939fa613abfeaaae3cf1bc8963562318d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 13 May 2015 18:55:14 +0200 Subject: [PATCH 22/72] Lower ADDMOD & MULMOD (limited) to a function call in the LLVM pass after optimization. --- libevmjit/Arith256.cpp | 183 +++++++++------------------------------- libevmjit/Arith256.h | 9 +- libevmjit/Compiler.cpp | 55 +++++++----- libevmjit/Optimizer.cpp | 10 +++ 4 files changed, 87 insertions(+), 170 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index 64ed7e649..472c699a8 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -86,6 +86,47 @@ llvm::Function* Arith256::getMulFunc(llvm::Module& _module) return func; } +llvm::Function* Arith256::getMul512Func(llvm::Module& _module) +{ + static const auto funcName = "evm.mul.i512"; + if (auto func = _module.getFunction(funcName)) + return func; + + auto i512Ty = llvm::IntegerType::get(_module.getContext(), 512); + auto func = llvm::Function::Create(llvm::FunctionType::get(i512Ty, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); + + auto x = &func->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); + + auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func); + auto builder = llvm::IRBuilder<>{bb}; + + auto i128 = builder.getIntNTy(128); + auto i256 = Type::Word; + auto x_lo = builder.CreateZExt(builder.CreateTrunc(x, i128, "x.lo"), i256); + auto y_lo = builder.CreateZExt(builder.CreateTrunc(y, i128, "y.lo"), i256); + auto x_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256); + auto y_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256); + + auto mul256Func = getMulFunc(_module); + auto t1 = builder.CreateCall(mul256Func, {x_lo, y_lo}); + auto t2 = builder.CreateCall(mul256Func, {x_lo, y_hi}); + auto t3 = builder.CreateCall(mul256Func, {x_hi, y_lo}); + auto t4 = builder.CreateCall(mul256Func, {x_hi, y_hi}); + + auto p = builder.CreateZExt(t1, i512Ty); + p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i512Ty), builder.getIntN(512, 128))); + p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i512Ty), builder.getIntN(512, 128))); + p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i512Ty), builder.getIntN(512, 256))); + builder.CreateRet(p); + + return func; +} + namespace { llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) @@ -354,47 +395,6 @@ llvm::Function* Arith256::getSRem256Func(llvm::Module& _module) return func; } -llvm::Function* Arith256::getMul512Func() -{ - auto& func = m_mul512; - if (!func) - { - auto i512 = m_builder.getIntNTy(512); - llvm::Type* argTypes[] = {Type::Word, Type::Word}; - func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule()); - func->setDoesNotThrow(); - func->setDoesNotAccessMemory(); - - auto x = &func->getArgumentList().front(); - x->setName("x"); - auto y = x->getNextNode(); - y->setName("y"); - - InsertPointGuard guard{m_builder}; - auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func); - m_builder.SetInsertPoint(bb); - auto i128 = m_builder.getIntNTy(128); - auto i256 = Type::Word; - auto x_lo = m_builder.CreateZExt(m_builder.CreateTrunc(x, i128, "x.lo"), i256); - auto y_lo = m_builder.CreateZExt(m_builder.CreateTrunc(y, i128, "y.lo"), i256); - auto x_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256); - auto y_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256); - - auto mul256Func = getMulFunc(*getModule()); - auto t1 = createCall(mul256Func, {x_lo, y_lo}); - auto t2 = createCall(mul256Func, {x_lo, y_hi}); - auto t3 = createCall(mul256Func, {x_hi, y_lo}); - auto t4 = createCall(mul256Func, {x_hi, y_hi}); - - auto p = m_builder.CreateZExt(t1, i512); - p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i512), m_builder.getIntN(512, 128))); - p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i512), m_builder.getIntN(512, 128))); - p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i512), m_builder.getIntN(512, 256))); - m_builder.CreateRet(p); - } - return func; -} - llvm::Function* Arith256::getExpFunc() { if (!m_exp) @@ -465,66 +465,6 @@ llvm::Function* Arith256::getExpFunc() return m_exp; } -llvm::Function* Arith256::getAddModFunc() -{ - if (!m_addmod) - { - auto i512Ty = m_builder.getIntNTy(512); - llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word}; - m_addmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "addmod", getModule()); - m_addmod->setDoesNotThrow(); - m_addmod->setDoesNotAccessMemory(); - - auto x = &m_addmod->getArgumentList().front(); - x->setName("x"); - auto y = x->getNextNode(); - y->setName("y"); - auto mod = y->getNextNode(); - mod->setName("m"); - - InsertPointGuard guard{m_builder}; - - auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_addmod); - m_builder.SetInsertPoint(entryBB); - auto x512 = m_builder.CreateZExt(x, i512Ty, "x512"); - auto y512 = m_builder.CreateZExt(y, i512Ty, "y512"); - auto m512 = m_builder.CreateZExt(mod, i512Ty, "m512"); - auto s = m_builder.CreateAdd(x512, y512, "s"); - auto r = createCall(getURem512Func(*getModule()), {s, m512}); - m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word)); - } - return m_addmod; -} - -llvm::Function* Arith256::getMulModFunc() -{ - if (!m_mulmod) - { - llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word}; - m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule()); - m_mulmod->setDoesNotThrow(); - m_mulmod->setDoesNotAccessMemory(); - - auto i512Ty = m_builder.getIntNTy(512); - auto x = &m_mulmod->getArgumentList().front(); - x->setName("x"); - auto y = x->getNextNode(); - y->setName("y"); - auto mod = y->getNextNode(); - mod->setName("mod"); - - InsertPointGuard guard{m_builder}; - - auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod); - m_builder.SetInsertPoint(entryBB); - auto p = createCall(getMul512Func(), {x, y}); - auto m = m_builder.CreateZExt(mod, i512Ty, "m"); - auto r = createCall(getURem512Func(*getModule()), {p, m}); - m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word)); - } - return m_mulmod; -} - llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) { // while (e != 0) { @@ -555,47 +495,6 @@ llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2) return createCall(getExpFunc(), {_arg1, _arg2}); } -llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) -{ - if (auto c1 = llvm::dyn_cast(_arg1)) - { - if (auto c2 = llvm::dyn_cast(_arg2)) - { - if (auto c3 = llvm::dyn_cast(_arg3)) - { - if (!c3->getValue()) - return Constant::get(0); - auto s = c1->getValue().zext(256+64) + c2->getValue().zext(256+64); - auto r = s.urem(c3->getValue().zext(256+64)).trunc(256); - return Constant::get(r); - } - } - } - - return createCall(getAddModFunc(), {_arg1, _arg2, _arg3}); -} - -llvm::Value* Arith256::mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3) -{ - if (auto c1 = llvm::dyn_cast(_arg1)) - { - if (auto c2 = llvm::dyn_cast(_arg2)) - { - if (auto c3 = llvm::dyn_cast(_arg3)) - { - if (!c3->getValue()) - return Constant::get(0); - auto p = c1->getValue().zext(512) * c2->getValue().zext(512); - auto r = p.urem(c3->getValue().zext(512)).trunc(256); - return Constant::get(r); - } - } - } - - return createCall(getMulModFunc(), {_arg1, _arg2, _arg3}); -} - - } } } diff --git a/libevmjit/Arith256.h b/libevmjit/Arith256.h index d6096a4c2..81535a792 100644 --- a/libevmjit/Arith256.h +++ b/libevmjit/Arith256.h @@ -15,12 +15,11 @@ public: Arith256(llvm::IRBuilder<>& _builder); llvm::Value* exp(llvm::Value* _arg1, llvm::Value* _arg2); - llvm::Value* mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); - llvm::Value* addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3); void debug(llvm::Value* _value, char _c); static llvm::Function* getMulFunc(llvm::Module& _module); + static llvm::Function* getMul512Func(llvm::Module& _module); static llvm::Function* getUDiv256Func(llvm::Module& _module); static llvm::Function* getURem256Func(llvm::Module& _module); static llvm::Function* getURem512Func(llvm::Module& _module); @@ -31,15 +30,9 @@ public: static llvm::Function* getUDivRem512Func(llvm::Module& _module); private: - llvm::Function* getMul512Func(); llvm::Function* getExpFunc(); - llvm::Function* getAddModFunc(); - llvm::Function* getMulModFunc(); - llvm::Function* m_mul512 = nullptr; llvm::Function* m_exp = nullptr; - llvm::Function* m_addmod = nullptr; - llvm::Function* m_mulmod = nullptr; llvm::Function* m_debug = nullptr; }; diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 33d802f90..56f1904a1 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -322,6 +322,41 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti break; } + case Instruction::ADDMOD: + { + auto i512Ty = m_builder.getIntNTy(512); + auto a = stack.pop(); + auto b = stack.pop(); + auto m = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0)); + a = m_builder.CreateZExt(a, i512Ty); + b = m_builder.CreateZExt(b, i512Ty); + m = m_builder.CreateZExt(m, i512Ty); + auto s = m_builder.CreateNUWAdd(a, b); + s = m_builder.CreateURem(s, m); + s = m_builder.CreateTrunc(s, Type::Word); + s = m_builder.CreateSelect(divByZero, Constant::get(0), s); + stack.push(s); + break; + } + + case Instruction::MULMOD: + { + auto i512Ty = m_builder.getIntNTy(512); + auto a = stack.pop(); + auto b = stack.pop(); + auto m = stack.pop(); + auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0)); + m = m_builder.CreateZExt(m, i512Ty); + // TODO: Add support for i256 x i256 -> i512 in LowerEVM pass + llvm::Value* p = m_builder.CreateCall(Arith256::getMul512Func(*_basicBlock.llvm()->getParent()->getParent()), {a, b}); + p = m_builder.CreateURem(p, m); + p = m_builder.CreateTrunc(p, Type::Word); + p = m_builder.CreateSelect(divByZero, Constant::get(0), p); + stack.push(p); + break; + } + case Instruction::EXP: { auto base = stack.pop(); @@ -440,26 +475,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti break; } - case Instruction::ADDMOD: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto mod = stack.pop(); - auto res = _arith.addmod(lhs, rhs, mod); - stack.push(res); - break; - } - - case Instruction::MULMOD: - { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto mod = stack.pop(); - auto res = _arith.mulmod(lhs, rhs, mod); - stack.push(res); - break; - } - case Instruction::SIGNEXTEND: { auto idx = stack.pop(); diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index 8eaab9a99..52bf14efa 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -54,6 +54,7 @@ bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb) { auto modified = false; auto module = _bb.getParent()->getParent(); + auto i512Ty = llvm::IntegerType::get(_bb.getContext(), 512); for (auto it = _bb.begin(); it != _bb.end(); ) { auto& inst = *it++; @@ -83,6 +84,15 @@ bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb) break; } } + else if (inst.getType() == i512Ty) + { + switch (inst.getOpcode()) + { + case llvm::Instruction::URem: + func = Arith256::getURem512Func(*module); + break; + } + } if (func) { From 499ab22637b8c4bc6614b2e94520a8fd1065ff30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 13 May 2015 19:12:49 +0200 Subject: [PATCH 23/72] Remove div by 0 check in udivrem implementation. --- libevmjit/Arith256.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index 472c699a8..6c1f8f160 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -154,10 +154,9 @@ llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func); auto builder = llvm::IRBuilder<>{entryBB}; - auto yNonZero = builder.CreateICmpNE(yArg, zero); auto yLEx = builder.CreateICmpULE(yArg, x); - auto r0 = builder.CreateSelect(yNonZero, x, zero, "r0"); - builder.CreateCondBr(builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB); + auto r0 = x; + builder.CreateCondBr(yLEx, mainBB, returnBB); builder.SetInsertPoint(mainBB); auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type); From 4992aa21c05a8476ea38d4fc2b207231b04d5b92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 14 May 2015 10:50:37 +0200 Subject: [PATCH 24/72] Remove unused code --- libevmjit/Stack.cpp | 90 --------------------------------------------- libevmjit/Stack.h | 3 -- 2 files changed, 93 deletions(-) diff --git a/libevmjit/Stack.cpp b/libevmjit/Stack.cpp index 7cf514dea..b47567f17 100644 --- a/libevmjit/Stack.cpp +++ b/libevmjit/Stack.cpp @@ -23,96 +23,6 @@ Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager): m_stack(_builder, "stack") {} -llvm::Function* Stack::getPushFunc() -{ - auto& func = m_push; - if (!func) - { - llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word}; - func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.push", getModule()); - llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::WordPtr}; - auto extPushFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_push", getModule()); - - auto rt = &func->getArgumentList().front(); - rt->setName("rt"); - auto value = rt->getNextNode(); - value->setName("value"); - - InsertPointGuard guard{m_builder}; - auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func); - m_builder.SetInsertPoint(entryBB); - auto a = m_builder.CreateAlloca(Type::Word); - m_builder.CreateStore(value, a); - createCall(extPushFunc, {rt, a}); - m_builder.CreateRetVoid(); - } - return func; -} - -llvm::Function* Stack::getSetFunc() -{ - auto& func = m_set; - if (!func) - { - llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::Word}; - func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.set", getModule()); - llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr}; - auto extSetFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_set", getModule()); - - auto rt = &func->getArgumentList().front(); - rt->setName("rt"); - auto index = rt->getNextNode(); - index->setName("index"); - auto value = index->getNextNode(); - value->setName("value"); - - InsertPointGuard guard{m_builder}; - auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func); - m_builder.SetInsertPoint(entryBB); - auto a = m_builder.CreateAlloca(Type::Word); - m_builder.CreateStore(value, a); - createCall(extSetFunc, {rt, index, a}); - m_builder.CreateRetVoid(); - } - return func; -} - -llvm::Function* Stack::getPopFunc() -{ - auto& func = m_pop; - if (!func) - { - llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr}; - func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.pop", getModule()); - llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size}; - auto extPopFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_pop", getModule()); - - auto rt = &func->getArgumentList().front(); - rt->setName("rt"); - auto index = rt->getNextNode(); - index->setName("index"); - auto jmpBuf = index->getNextNode(); - jmpBuf->setName("jmpBuf"); - - InsertPointGuard guard{m_builder}; - auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func); - auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func); - auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func); - - m_builder.SetInsertPoint(entryBB); - auto ok = createCall(extPopFunc, {rt, index}); - m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight - - m_builder.SetInsertPoint(underflowBB); - m_runtimeManager.abort(jmpBuf); - m_builder.CreateUnreachable(); - - m_builder.SetInsertPoint(returnBB); - m_builder.CreateRetVoid(); - } - return func; -} - llvm::Function* Stack::getGetFunc() { auto& func = m_get; diff --git a/libevmjit/Stack.h b/libevmjit/Stack.h index 3c526f0d3..6f7ad1c0b 100644 --- a/libevmjit/Stack.h +++ b/libevmjit/Stack.h @@ -24,10 +24,7 @@ public: void free() { m_stack.free(); } private: - llvm::Function* getPopFunc(); - llvm::Function* getPushFunc(); llvm::Function* getGetFunc(); - llvm::Function* getSetFunc(); RuntimeManager& m_runtimeManager; From edfb49e3efe9016a8f4f7ad130e286ec8710d989 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 14 May 2015 16:14:21 +0200 Subject: [PATCH 25/72] Add protection against hardware division by 0. --- libevmjit/Compiler.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 56f1904a1..75c81f9ed 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -280,6 +280,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto d = stack.pop(); auto n = stack.pop(); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal auto r = m_builder.CreateUDiv(d, n); r = m_builder.CreateSelect(divByZero, Constant::get(0), r); stack.push(r); @@ -292,6 +293,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto n = stack.pop(); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1)); + n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal auto r = m_builder.CreateSDiv(d, n); r = m_builder.CreateSelect(divByZero, Constant::get(0), r); auto dNeg = m_builder.CreateSub(Constant::get(0), d); @@ -305,6 +307,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto d = stack.pop(); auto n = stack.pop(); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal auto r = m_builder.CreateURem(d, n); r = m_builder.CreateSelect(divByZero, Constant::get(0), r); stack.push(r); @@ -316,6 +319,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto d = stack.pop(); auto n = stack.pop(); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal auto r = m_builder.CreateSRem(d, n); r = m_builder.CreateSelect(divByZero, Constant::get(0), r); stack.push(r); From 25b080ee87a4865ea6f41c0772dd4807b1bc7097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 14 May 2015 16:39:16 +0200 Subject: [PATCH 26/72] Handle more edge cases in DIV, etc. --- libevmjit/Compiler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 75c81f9ed..a7006dcde 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -297,7 +297,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto r = m_builder.CreateSDiv(d, n); r = m_builder.CreateSelect(divByZero, Constant::get(0), r); auto dNeg = m_builder.CreateSub(Constant::get(0), d); - r = m_builder.CreateSelect(divByMinusOne, dNeg, r); + r = m_builder.CreateSelect(divByMinusOne, dNeg, r); // protect against undef i256.min / -1 stack.push(r); break; } @@ -319,9 +319,11 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto d = stack.pop(); auto n = stack.pop(); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0)); + auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1)); n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal auto r = m_builder.CreateSRem(d, n); r = m_builder.CreateSelect(divByZero, Constant::get(0), r); + r = m_builder.CreateSelect(divByMinusOne, Constant::get(0), r); // protect against undef i256.min / -1 stack.push(r); break; } From a0ca9732744712947b641d05a04c5d4196c7aec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 14 May 2015 16:57:26 +0200 Subject: [PATCH 27/72] Enable function inlining optimization. --- libevmjit/Optimizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index 52bf14efa..d5bf7f05c 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -21,7 +21,7 @@ namespace jit bool optimize(llvm::Module& _module) { auto pm = llvm::legacy::PassManager{}; - //pm.add(llvm::createFunctionInliningPass(2, 2)); // Problem with APInt value bigger than 64bit + pm.add(llvm::createFunctionInliningPass(2, 2)); pm.add(llvm::createCFGSimplificationPass()); pm.add(llvm::createInstructionCombiningPass()); pm.add(llvm::createAggressiveDCEPass()); From e0c176f377654e509cdbe1e2e8c6c1005875b0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 15 May 2015 14:06:42 +0200 Subject: [PATCH 28/72] Extract execution context from ExecutionEngine::run. --- libevmjit-cpp/JitVM.cpp | 5 +- libevmjit-cpp/JitVM.h | 2 +- libevmjit/ExecutionEngine.cpp | 86 ++++++++++++++++++++--------------- libevmjit/ExecutionEngine.h | 24 +++++++--- libevmjit/interface.cpp | 14 +++--- 5 files changed, 76 insertions(+), 55 deletions(-) diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 7acbec5c1..85109ebbc 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include "Utils.h" @@ -56,7 +55,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) m_data.codeHash = eth2llvm(sha3(_ext.code)); auto env = reinterpret_cast(&_ext); - auto exitCode = m_engine.run(&m_data, env); + auto exitCode = jit::ExecutionEngine::run(m_context, &m_data, env); switch (exitCode) { case ReturnCode::Suicide: @@ -79,7 +78,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) } m_gas = m_data.gas; // TODO: Remove m_gas field - return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)}; + return {std::get<0>(m_context.returnData), std::get<1>(m_context.returnData)}; } } diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 58caa3648..4bd4a7bbf 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -17,7 +17,7 @@ private: explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} jit::RuntimeData m_data; - jit::ExecutionEngine m_engine; + jit::ExecutionContext m_context; std::unique_ptr m_fallbackVM; ///< VM used in case of input data rejected by JIT }; diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index 0377ac402..741a7d764 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -90,65 +90,77 @@ void parseOptions() cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); } +// FIXME: It is temporary, becaue ExecutionEngine.h is currently our public header +// and including llvm::ExecutionEngine there is not a good idea. +llvm::ExecutionEngine* g_ee = nullptr; + } +ExecutionEngine& ExecutionEngine::get() +{ + static ExecutionEngine instance; + return instance; +} -ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) +ExecutionEngine::ExecutionEngine() { - static std::once_flag flag; - std::call_once(flag, parseOptions); + /// ExecutionEngine is created only once - std::unique_ptr listener{new ExecStats}; - listener->stateChanged(ExecState::Started); + parseOptions(); + + if (g_cache == CacheMode::clear) + Cache::clear(); bool preloadCache = g_cache == CacheMode::preload; if (preloadCache) g_cache = CacheMode::on; - // TODO: Do not pseudo-init the cache every time - auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, listener.get()) : nullptr; + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); - static std::unique_ptr ee; - if (!ee) - { - if (g_cache == CacheMode::clear) - Cache::clear(); + auto module = std::unique_ptr(new llvm::Module({}, llvm::getGlobalContext())); + + // FIXME: LLVM 3.7: test on Windows + auto triple = llvm::Triple(llvm::sys::getProcessTriple()); + if (triple.getOS() == llvm::Triple::OSType::Win32) + triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format + module->setTargetTriple(triple.str()); - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); + llvm::EngineBuilder builder(std::move(module)); + builder.setEngineKind(llvm::EngineKind::JIT); + builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); - auto module = std::unique_ptr(new llvm::Module({}, llvm::getGlobalContext())); + g_ee = (builder.create()); - // FIXME: LLVM 3.7: test on Windows - auto triple = llvm::Triple(llvm::sys::getProcessTriple()); - if (triple.getOS() == llvm::Triple::OSType::Win32) - triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format - module->setTargetTriple(triple.str()); + // TODO: Update cache listener + auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, nullptr) : nullptr; + g_ee->setObjectCache(objectCache); - llvm::EngineBuilder builder(std::move(module)); - builder.setEngineKind(llvm::EngineKind::JIT); - builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); + // FIXME: Disabled during API changes + //if (preloadCache) + // Cache::preload(*ee, funcCache); +} + + +ReturnCode ExecutionEngine::run(ExecutionContext& _context, RuntimeData* _data, Env* _env) +{ + ExecutionEngine::get(); // FIXME + + std::unique_ptr listener{new ExecStats}; + listener->stateChanged(ExecState::Started); - ee.reset(builder.create()); - if (!CHECK(ee)) - return ReturnCode::LLVMConfigError; - ee->setObjectCache(objectCache); - // FIXME: Disabled during API changes - //if (preloadCache) - // Cache::preload(*ee, funcCache); - } static StatsCollector statsCollector; auto mainFuncName = codeHash(_data->codeHash); - m_runtime.init(_data, _env); + _context.m_runtime.init(_data, _env); // TODO: Remove cast auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(_data->codeHash); if (!entryFuncPtr) { - auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr; + auto module = Cache::getObject(mainFuncName); if (!module) { listener->stateChanged(ExecState::Compilation); @@ -166,20 +178,20 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) if (g_dump) module->dump(); - ee->addModule(std::move(module)); + g_ee->addModule(std::move(module)); listener->stateChanged(ExecState::CodeGen); - entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); + entryFuncPtr = (EntryFuncPtr)g_ee->getFunctionAddress(mainFuncName); if (!CHECK(entryFuncPtr)) return ReturnCode::LLVMLinkError; JIT::mapCode(_data->codeHash, (void*)entryFuncPtr); // FIXME: Remove cast } listener->stateChanged(ExecState::Execution); - auto returnCode = entryFuncPtr(&m_runtime); + auto returnCode = entryFuncPtr(&_context.m_runtime); listener->stateChanged(ExecState::Return); if (returnCode == ReturnCode::Return) - returnData = m_runtime.getReturnData(); // Save reference to return data + _context.returnData = _context.m_runtime.getReturnData(); // Save reference to return data listener->stateChanged(ExecState::Finished); diff --git a/libevmjit/ExecutionEngine.h b/libevmjit/ExecutionEngine.h index 26da6977c..c380b6cb5 100644 --- a/libevmjit/ExecutionEngine.h +++ b/libevmjit/ExecutionEngine.h @@ -38,22 +38,32 @@ public: virtual void stateChanged(ExecState) {} }; -class ExecutionEngine +class ExecutionContext { public: - ExecutionEngine() = default; - ExecutionEngine(ExecutionEngine const&) = delete; - ExecutionEngine& operator=(ExecutionEngine) = delete; - - EXPORT ReturnCode run(RuntimeData* _data, Env* _env); + ExecutionContext() = default; + ExecutionContext(ExecutionContext const&) = delete; + ExecutionContext& operator=(ExecutionContext const&) = delete; /// Reference to returned data (RETURN opcode used) bytes_ref returnData; -private: Runtime m_runtime; }; +class ExecutionEngine +{ +public: + ExecutionEngine(ExecutionEngine const&) = delete; + ExecutionEngine& operator=(ExecutionEngine const&) = delete; + + EXPORT static ReturnCode run(ExecutionContext& _context, RuntimeData* _data, Env* _env); + +private: + ExecutionEngine(); + static ExecutionEngine& get(); +}; + } } } diff --git a/libevmjit/interface.cpp b/libevmjit/interface.cpp index 01f743a2e..bc9d98474 100644 --- a/libevmjit/interface.cpp +++ b/libevmjit/interface.cpp @@ -7,23 +7,23 @@ using namespace dev::eth::jit; EXPORT void* evmjit_create() noexcept { - // TODO: Make sure ExecutionEngine constructor does not throw - return new(std::nothrow) ExecutionEngine; + // TODO: Make sure ExecutionEngine constructor does not throw + make JIT/ExecutionEngine interface all nothrow + return new(std::nothrow) ExecutionContext; } -EXPORT void evmjit_destroy(ExecutionEngine* _engine) noexcept +EXPORT void evmjit_destroy(ExecutionContext* _context) noexcept { - delete _engine; + delete _context; } -EXPORT int evmjit_run(ExecutionEngine* _engine, RuntimeData* _data, Env* _env) noexcept +EXPORT int evmjit_run(ExecutionContext* _context, RuntimeData* _data, Env* _env) noexcept { - if (!_engine || !_data) + if (!_context || !_data) return static_cast(ReturnCode::UnexpectedException); try { - auto returnCode = _engine->run(_data, _env); + auto returnCode = ExecutionEngine::run(*_context, _data, _env); return static_cast(returnCode); } catch(...) From c56858f4989f8c953c45a8d28b4e698b6bf4e288 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 15 May 2015 15:22:37 +0200 Subject: [PATCH 29/72] Merge ExecutionContext and Runtime classes --- libevmjit-cpp/JitVM.cpp | 4 +- libevmjit-cpp/JitVM.h | 1 + libevmjit/Array.cpp | 1 - libevmjit/CMakeLists.txt | 2 +- libevmjit/Common.h | 4 +- .../{Runtime.cpp => ExecutionContext.cpp} | 18 ++------ libevmjit/ExecutionContext.h | 41 +++++++++++++++++++ libevmjit/ExecutionEngine.cpp | 27 ++++++------ libevmjit/ExecutionEngine.h | 22 ++++------ libevmjit/Memory.cpp | 1 - libevmjit/Runtime.h | 30 -------------- libevmjit/Stack.cpp | 1 - libevmjit/interface.cpp | 15 +++---- 13 files changed, 80 insertions(+), 87 deletions(-) rename libevmjit/{Runtime.cpp => ExecutionContext.cpp} (70%) create mode 100644 libevmjit/ExecutionContext.h delete mode 100644 libevmjit/Runtime.h diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 85109ebbc..e44628c67 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -54,8 +54,8 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) m_data.codeSize = _ext.code.size(); m_data.codeHash = eth2llvm(sha3(_ext.code)); - auto env = reinterpret_cast(&_ext); - auto exitCode = jit::ExecutionEngine::run(m_context, &m_data, env); + m_context.init(m_data, reinterpret_cast(&_ext)); + auto exitCode = jit::ExecutionEngine::run(m_context); switch (exitCode) { case ReturnCode::Suicide: diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 4bd4a7bbf..aad379a75 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include namespace dev diff --git a/libevmjit/Array.cpp b/libevmjit/Array.cpp index b3d3cc0bb..a76776c69 100644 --- a/libevmjit/Array.cpp +++ b/libevmjit/Array.cpp @@ -6,7 +6,6 @@ #include "preprocessor/llvm_includes_end.h" #include "RuntimeManager.h" -#include "Runtime.h" #include "Utils.h" #include // DEBUG only diff --git a/libevmjit/CMakeLists.txt b/libevmjit/CMakeLists.txt index 4b15d52ac..324ad6717 100644 --- a/libevmjit/CMakeLists.txt +++ b/libevmjit/CMakeLists.txt @@ -11,6 +11,7 @@ set(SOURCES ${EVMJIT_INCLUDE_DIR}/evmjit/DataTypes.h Endianness.cpp Endianness.h ExecStats.cpp ExecStats.h + ExecutionContext.cpp ExecutionContext.h ExecutionEngine.cpp ExecutionEngine.h Ext.cpp Ext.h GasMeter.cpp GasMeter.h @@ -19,7 +20,6 @@ set(SOURCES JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h Memory.cpp Memory.h Optimizer.cpp Optimizer.h - Runtime.cpp Runtime.h RuntimeData.h RuntimeManager.cpp RuntimeManager.h Stack.cpp Stack.h diff --git a/libevmjit/Common.h b/libevmjit/Common.h index b519614fe..e99308174 100644 --- a/libevmjit/Common.h +++ b/libevmjit/Common.h @@ -4,8 +4,8 @@ #include #ifdef _MSC_VER -#define EXPORT __declspec(dllexport) -#define _ALLOW_KEYWORD_MACROS +#define EXPORT __declspec(dllexport) +#define _ALLOW_KEYWORD_MACROS #define noexcept throw() #else #define EXPORT diff --git a/libevmjit/Runtime.cpp b/libevmjit/ExecutionContext.cpp similarity index 70% rename from libevmjit/Runtime.cpp rename to libevmjit/ExecutionContext.cpp index 7e9a7d52e..fab6fad87 100644 --- a/libevmjit/Runtime.cpp +++ b/libevmjit/ExecutionContext.cpp @@ -1,29 +1,20 @@ -#include "Runtime.h" - +#include "ExecutionContext.h" #include namespace dev { -namespace eth -{ -namespace jit -{ - -void Runtime::init(RuntimeData* _data, Env* _env) +namespace evmjit { - m_data = _data; - m_env = _env; -} extern "C" void ext_free(void* _data) noexcept; -Runtime::~Runtime() +ExecutionContext::~ExecutionContext() { if (m_memData) ext_free(m_memData); // Use helper free to check memory leaks } -bytes_ref Runtime::getReturnData() const +bytes_ref ExecutionContext::getReturnData() const { auto data = m_data->callData; auto size = static_cast(m_data->callDataSize); @@ -40,4 +31,3 @@ bytes_ref Runtime::getReturnData() const } } -} diff --git a/libevmjit/ExecutionContext.h b/libevmjit/ExecutionContext.h new file mode 100644 index 000000000..6d40e2819 --- /dev/null +++ b/libevmjit/ExecutionContext.h @@ -0,0 +1,41 @@ +#pragma once + +#include "RuntimeData.h" + +namespace dev +{ +namespace evmjit +{ + using namespace eth::jit; // FIXME + +class ExecutionContext +{ +public: + ExecutionContext() = default; + ExecutionContext(RuntimeData& _data, Env* _env) { init(_data, _env); } + ExecutionContext(ExecutionContext const&) = delete; + ExecutionContext& operator=(ExecutionContext const&) = delete; + EXPORT ~ExecutionContext(); + + void init(RuntimeData& _data, Env* _env) { m_data = &_data; m_env = _env; } + + byte const* code() const { return m_data->code; } + uint64_t codeSize() const { return m_data->codeSize; } + h256 const& codeHash() const { return m_data->codeHash; } + + bytes_ref getReturnData() const; + +private: + RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract. + Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract. + byte* m_memData = nullptr; + uint64_t m_memSize = 0; + uint64_t m_memCap = 0; + +public: + /// Reference to returned data (RETURN opcode used) + bytes_ref returnData; +}; + +} +} diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index 741a7d764..a96ac700c 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -19,8 +19,8 @@ #include #include "preprocessor/llvm_includes_end.h" +#include "ExecutionContext.h" #include "evmjit/JIT.h" -#include "Runtime.h" #include "Compiler.h" #include "Optimizer.h" #include "Cache.h" @@ -38,9 +38,9 @@ using evmjit::JIT; namespace { -using EntryFuncPtr = ReturnCode(*)(Runtime*); +using EntryFuncPtr = ReturnCode(*)(ExecutionContext*); -std::string codeHash(i256 const& _hash) +std::string hash2str(i256 const& _hash) { static const auto size = sizeof(_hash); static const auto hexChars = "0123456789abcdef"; @@ -142,30 +142,31 @@ ExecutionEngine::ExecutionEngine() } -ReturnCode ExecutionEngine::run(ExecutionContext& _context, RuntimeData* _data, Env* _env) +ReturnCode ExecutionEngine::run(ExecutionContext& _context) { ExecutionEngine::get(); // FIXME std::unique_ptr listener{new ExecStats}; listener->stateChanged(ExecState::Started); - + auto code = _context.code(); + auto codeSize = _context.codeSize(); + auto codeHash = _context.codeHash(); static StatsCollector statsCollector; - auto mainFuncName = codeHash(_data->codeHash); - _context.m_runtime.init(_data, _env); + auto mainFuncName = hash2str(codeHash); // TODO: Remove cast - auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(_data->codeHash); + auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(codeHash); if (!entryFuncPtr) { auto module = Cache::getObject(mainFuncName); if (!module) { listener->stateChanged(ExecState::Compilation); - assert(_data->code || !_data->codeSize); //TODO: Is it good idea to execute empty code? - module = Compiler{{}}.compile(_data->code, _data->code + _data->codeSize, mainFuncName); + assert(code || !codeSize); //TODO: Is it good idea to execute empty code? + module = Compiler{{}}.compile(code, code + codeSize, mainFuncName); if (g_optimize) { @@ -183,15 +184,15 @@ ReturnCode ExecutionEngine::run(ExecutionContext& _context, RuntimeData* _data, entryFuncPtr = (EntryFuncPtr)g_ee->getFunctionAddress(mainFuncName); if (!CHECK(entryFuncPtr)) return ReturnCode::LLVMLinkError; - JIT::mapCode(_data->codeHash, (void*)entryFuncPtr); // FIXME: Remove cast + JIT::mapCode(codeHash, (void*)entryFuncPtr); // FIXME: Remove cast } listener->stateChanged(ExecState::Execution); - auto returnCode = entryFuncPtr(&_context.m_runtime); + auto returnCode = entryFuncPtr(&_context); listener->stateChanged(ExecState::Return); if (returnCode == ReturnCode::Return) - _context.returnData = _context.m_runtime.getReturnData(); // Save reference to return data + _context.returnData = _context.getReturnData(); // Save reference to return data listener->stateChanged(ExecState::Finished); diff --git a/libevmjit/ExecutionEngine.h b/libevmjit/ExecutionEngine.h index c380b6cb5..5c689403c 100644 --- a/libevmjit/ExecutionEngine.h +++ b/libevmjit/ExecutionEngine.h @@ -1,15 +1,20 @@ #pragma once #include +#include "Common.h" -#include "Runtime.h" namespace dev { +namespace evmjit +{ + class ExecutionContext; +} namespace eth { namespace jit { + using namespace evmjit; // FIXME enum class ExecState { @@ -38,26 +43,13 @@ public: virtual void stateChanged(ExecState) {} }; -class ExecutionContext -{ -public: - ExecutionContext() = default; - ExecutionContext(ExecutionContext const&) = delete; - ExecutionContext& operator=(ExecutionContext const&) = delete; - - /// Reference to returned data (RETURN opcode used) - bytes_ref returnData; - - Runtime m_runtime; -}; - class ExecutionEngine { public: ExecutionEngine(ExecutionEngine const&) = delete; ExecutionEngine& operator=(ExecutionEngine const&) = delete; - EXPORT static ReturnCode run(ExecutionContext& _context, RuntimeData* _data, Env* _env); + EXPORT static ReturnCode run(ExecutionContext& _context); private: ExecutionEngine(); diff --git a/libevmjit/Memory.cpp b/libevmjit/Memory.cpp index 6d5b72024..fdf5a6b98 100644 --- a/libevmjit/Memory.cpp +++ b/libevmjit/Memory.cpp @@ -5,7 +5,6 @@ #include "preprocessor/llvm_includes_end.h" #include "Type.h" -#include "Runtime.h" #include "GasMeter.h" #include "Endianness.h" #include "RuntimeManager.h" diff --git a/libevmjit/Runtime.h b/libevmjit/Runtime.h deleted file mode 100644 index 895128a59..000000000 --- a/libevmjit/Runtime.h +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include "RuntimeData.h" - -namespace dev -{ -namespace eth -{ -namespace jit -{ - -class Runtime -{ -public: - void init(RuntimeData* _data, Env* _env); - EXPORT ~Runtime(); - - bytes_ref getReturnData() const; - -private: - RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract. - Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract. - byte* m_memData = nullptr; - uint64_t m_memSize = 0; - uint64_t m_memCap = 0; -}; - -} -} -} diff --git a/libevmjit/Stack.cpp b/libevmjit/Stack.cpp index b47567f17..b7b8f25b1 100644 --- a/libevmjit/Stack.cpp +++ b/libevmjit/Stack.cpp @@ -5,7 +5,6 @@ #include "preprocessor/llvm_includes_end.h" #include "RuntimeManager.h" -#include "Runtime.h" #include "Utils.h" #include // DEBUG only diff --git a/libevmjit/interface.cpp b/libevmjit/interface.cpp index bc9d98474..18b36cd7f 100644 --- a/libevmjit/interface.cpp +++ b/libevmjit/interface.cpp @@ -1,14 +1,18 @@ #include "ExecutionEngine.h" +#include "ExecutionContext.h" extern "C" { using namespace dev::eth::jit; -EXPORT void* evmjit_create() noexcept +EXPORT void* evmjit_create(RuntimeData* _data, Env* _env) noexcept { + if (!_data) + return nullptr; + // TODO: Make sure ExecutionEngine constructor does not throw + make JIT/ExecutionEngine interface all nothrow - return new(std::nothrow) ExecutionContext; + return new(std::nothrow) ExecutionContext{*_data, _env}; } EXPORT void evmjit_destroy(ExecutionContext* _context) noexcept @@ -16,14 +20,11 @@ EXPORT void evmjit_destroy(ExecutionContext* _context) noexcept delete _context; } -EXPORT int evmjit_run(ExecutionContext* _context, RuntimeData* _data, Env* _env) noexcept +EXPORT int evmjit_run(ExecutionContext* _context) noexcept { - if (!_context || !_data) - return static_cast(ReturnCode::UnexpectedException); - try { - auto returnCode = ExecutionEngine::run(*_context, _data, _env); + auto returnCode = ExecutionEngine::run(*_context); return static_cast(returnCode); } catch(...) From c66a7e49d3d7159d337a8cdd613e039727317410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 15 May 2015 17:05:11 +0200 Subject: [PATCH 30/72] Fix cache mode handling. --- libevmjit/Cache.cpp | 17 ++++++++++++++--- libevmjit/Cache.h | 2 +- libevmjit/ExecutionEngine.cpp | 6 +----- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/libevmjit/Cache.cpp b/libevmjit/Cache.cpp index aa316d51d..464912104 100644 --- a/libevmjit/Cache.cpp +++ b/libevmjit/Cache.cpp @@ -40,12 +40,23 @@ namespace } } -ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener) +ObjectCache* Cache::init(CacheMode _mode, ExecutionEngineListener* _listener) { - static ObjectCache objectCache; g_mode = _mode; g_listener = _listener; - return &objectCache; + + if (g_mode == CacheMode::clear) + { + Cache::clear(); + g_mode = CacheMode::off; + } + + if (g_mode != CacheMode::off) + { + static ObjectCache objectCache; + return &objectCache; + } + return nullptr; } void Cache::clear() diff --git a/libevmjit/Cache.h b/libevmjit/Cache.h index 6b8457f4b..ef4a0bac7 100644 --- a/libevmjit/Cache.h +++ b/libevmjit/Cache.h @@ -45,7 +45,7 @@ public: class Cache { public: - static ObjectCache* getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener); + static ObjectCache* init(CacheMode _mode, ExecutionEngineListener* _listener); static std::unique_ptr getObject(std::string const& id); /// Clears cache storage diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index a96ac700c..736946079 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -108,9 +108,6 @@ ExecutionEngine::ExecutionEngine() parseOptions(); - if (g_cache == CacheMode::clear) - Cache::clear(); - bool preloadCache = g_cache == CacheMode::preload; if (preloadCache) g_cache = CacheMode::on; @@ -133,8 +130,7 @@ ExecutionEngine::ExecutionEngine() g_ee = (builder.create()); // TODO: Update cache listener - auto objectCache = (g_cache != CacheMode::off && g_cache != CacheMode::clear) ? Cache::getObjectCache(g_cache, nullptr) : nullptr; - g_ee->setObjectCache(objectCache); + g_ee->setObjectCache(Cache::init(g_cache, nullptr)); // FIXME: Disabled during API changes //if (preloadCache) From b8fb3a138c1c81f5823911e62d2dbe7b9c8e3ee9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 15 May 2015 17:31:08 +0200 Subject: [PATCH 31/72] Better style of ExecutionEngine initialization. Some eth::jit -> evmjit namespace transfers. --- include/evmjit/JIT.h | 10 +--------- libevmjit-cpp/JitVM.cpp | 18 ++++++++---------- libevmjit-cpp/JitVM.h | 2 +- libevmjit/Cache.cpp | 6 ++---- libevmjit/Cache.h | 5 +---- libevmjit/ExecStats.cpp | 5 +---- libevmjit/ExecStats.h | 6 ++---- libevmjit/ExecutionEngine.cpp | 35 +++++++++++------------------------ libevmjit/ExecutionEngine.h | 20 +++----------------- libevmjit/interface.cpp | 2 +- 10 files changed, 31 insertions(+), 78 deletions(-) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index 446dd9e56..ba7a0dbc0 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -4,14 +4,6 @@ namespace dev { -namespace eth -{ -namespace jit -{ - class ExecutionEngine; -} -} - namespace evmjit { @@ -26,7 +18,7 @@ public: static bool isCodeReady(h256 _codeHash); private: - friend class dev::eth::jit::ExecutionEngine; + friend class ExecutionEngine; static void* getCode(h256 _codeHash); static void mapCode(h256 _codeHash, void* _funcAddr); diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index e44628c67..a4eed5793 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -19,8 +19,6 @@ extern "C" void env_sload(); // fake declaration for linker symbol stripping wor bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) { - using namespace jit; - auto rejected = false; // TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope rejected |= m_gas > std::numeric_limits::max(); // Do not accept requests with gas > 2^63 (int64 max) @@ -54,23 +52,23 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) m_data.codeSize = _ext.code.size(); m_data.codeHash = eth2llvm(sha3(_ext.code)); - m_context.init(m_data, reinterpret_cast(&_ext)); - auto exitCode = jit::ExecutionEngine::run(m_context); + m_context.init(m_data, reinterpret_cast(&_ext)); + auto exitCode = evmjit::ExecutionEngine::run(m_context); switch (exitCode) { - case ReturnCode::Suicide: + case evmjit::ReturnCode::Suicide: _ext.suicide(right160(llvm2eth(m_data.address))); break; - case ReturnCode::BadJumpDestination: + case evmjit::ReturnCode::BadJumpDestination: BOOST_THROW_EXCEPTION(BadJumpDestination()); - case ReturnCode::OutOfGas: + case evmjit::ReturnCode::OutOfGas: BOOST_THROW_EXCEPTION(OutOfGas()); - case ReturnCode::StackUnderflow: + case evmjit::ReturnCode::StackUnderflow: // FIXME: Remove support for detail errors BOOST_THROW_EXCEPTION(StackUnderflow()); - case ReturnCode::BadInstruction: + case evmjit::ReturnCode::BadInstruction: BOOST_THROW_EXCEPTION(BadInstruction()); - case ReturnCode::LinkerWorkaround: // never happens + case evmjit::ReturnCode::LinkerWorkaround: // never happens env_sload(); // but forces linker to include env_* JIT callback functions break; default: diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index aad379a75..a6f6f1d52 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -18,7 +18,7 @@ private: explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} jit::RuntimeData m_data; - jit::ExecutionContext m_context; + evmjit::ExecutionContext m_context; std::unique_ptr m_fallbackVM; ///< VM used in case of input data rejected by JIT }; diff --git a/libevmjit/Cache.cpp b/libevmjit/Cache.cpp index 464912104..21be82d11 100644 --- a/libevmjit/Cache.cpp +++ b/libevmjit/Cache.cpp @@ -16,10 +16,9 @@ namespace dev { -namespace eth -{ -namespace jit +namespace evmjit { + using namespace eth::jit; namespace { @@ -177,4 +176,3 @@ std::unique_ptr ObjectCache::getObject(llvm::Module const* _ } } -} diff --git a/libevmjit/Cache.h b/libevmjit/Cache.h index ef4a0bac7..61635b8a1 100644 --- a/libevmjit/Cache.h +++ b/libevmjit/Cache.h @@ -12,9 +12,7 @@ namespace llvm namespace dev { -namespace eth -{ -namespace jit +namespace evmjit { class ExecutionEngineListener; @@ -57,4 +55,3 @@ public: } } -} diff --git a/libevmjit/ExecStats.cpp b/libevmjit/ExecStats.cpp index ff8c05307..c7f6ef0cd 100644 --- a/libevmjit/ExecStats.cpp +++ b/libevmjit/ExecStats.cpp @@ -8,9 +8,7 @@ namespace dev { -namespace eth -{ -namespace jit +namespace evmjit { void ExecStats::stateChanged(ExecState _state) @@ -95,4 +93,3 @@ StatsCollector::~StatsCollector() } } -} diff --git a/libevmjit/ExecStats.h b/libevmjit/ExecStats.h index 0451ccb05..3ea77733b 100644 --- a/libevmjit/ExecStats.h +++ b/libevmjit/ExecStats.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -8,9 +9,7 @@ namespace dev { -namespace eth -{ -namespace jit +namespace evmjit { class ExecStats : public ExecutionEngineListener @@ -42,4 +41,3 @@ public: } } -} diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index 736946079..e424d9d3a 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -30,11 +30,8 @@ namespace dev { -namespace eth +namespace evmjit { -namespace jit -{ -using evmjit::JIT; namespace { @@ -56,7 +53,7 @@ std::string hash2str(i256 const& _hash) return str; } -void printVersion() +void printVersion() // FIXME: Fix LLVM version parsing { std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n" << " EVMJIT version " << EVMJIT_VERSION << "\n" @@ -90,19 +87,7 @@ void parseOptions() cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); } -// FIXME: It is temporary, becaue ExecutionEngine.h is currently our public header -// and including llvm::ExecutionEngine there is not a good idea. -llvm::ExecutionEngine* g_ee = nullptr; - -} - -ExecutionEngine& ExecutionEngine::get() -{ - static ExecutionEngine instance; - return instance; -} - -ExecutionEngine::ExecutionEngine() +std::unique_ptr init() { /// ExecutionEngine is created only once @@ -127,20 +112,23 @@ ExecutionEngine::ExecutionEngine() builder.setEngineKind(llvm::EngineKind::JIT); builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); - g_ee = (builder.create()); + auto ee = std::unique_ptr{builder.create()}; // TODO: Update cache listener - g_ee->setObjectCache(Cache::init(g_cache, nullptr)); + ee->setObjectCache(Cache::init(g_cache, nullptr)); // FIXME: Disabled during API changes //if (preloadCache) // Cache::preload(*ee, funcCache); + + return ee; } +} ReturnCode ExecutionEngine::run(ExecutionContext& _context) { - ExecutionEngine::get(); // FIXME + static auto s_ee = init(); std::unique_ptr listener{new ExecStats}; listener->stateChanged(ExecState::Started); @@ -175,9 +163,9 @@ ReturnCode ExecutionEngine::run(ExecutionContext& _context) if (g_dump) module->dump(); - g_ee->addModule(std::move(module)); + s_ee->addModule(std::move(module)); listener->stateChanged(ExecState::CodeGen); - entryFuncPtr = (EntryFuncPtr)g_ee->getFunctionAddress(mainFuncName); + entryFuncPtr = (EntryFuncPtr)s_ee->getFunctionAddress(mainFuncName); if (!CHECK(entryFuncPtr)) return ReturnCode::LLVMLinkError; JIT::mapCode(codeHash, (void*)entryFuncPtr); // FIXME: Remove cast @@ -200,4 +188,3 @@ ReturnCode ExecutionEngine::run(ExecutionContext& _context) } } -} diff --git a/libevmjit/ExecutionEngine.h b/libevmjit/ExecutionEngine.h index 5c689403c..d14e50702 100644 --- a/libevmjit/ExecutionEngine.h +++ b/libevmjit/ExecutionEngine.h @@ -1,20 +1,14 @@ #pragma once -#include #include "Common.h" - namespace dev { namespace evmjit { - class ExecutionContext; -} -namespace eth -{ -namespace jit -{ - using namespace evmjit; // FIXME +class ExecutionContext; + +using namespace eth::jit; enum class ExecState { @@ -46,16 +40,8 @@ public: class ExecutionEngine { public: - ExecutionEngine(ExecutionEngine const&) = delete; - ExecutionEngine& operator=(ExecutionEngine const&) = delete; - EXPORT static ReturnCode run(ExecutionContext& _context); - -private: - ExecutionEngine(); - static ExecutionEngine& get(); }; } } -} diff --git a/libevmjit/interface.cpp b/libevmjit/interface.cpp index 18b36cd7f..3b4145cb7 100644 --- a/libevmjit/interface.cpp +++ b/libevmjit/interface.cpp @@ -3,8 +3,8 @@ extern "C" { - using namespace dev::eth::jit; +using namespace dev::evmjit; EXPORT void* evmjit_create(RuntimeData* _data, Env* _env) noexcept { From ccdb74eb0b9f78c93b4206cb55858c3669261aec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 18 May 2015 09:48:44 +0200 Subject: [PATCH 32/72] Code migration. --- include/evmjit/DataTypes.h | 12 ++++++ include/evmjit/JIT.h | 6 --- libevmjit-cpp/JitVM.h | 2 +- libevmjit/Arith256.cpp | 1 + libevmjit/BasicBlock.h | 4 +- libevmjit/CMakeLists.txt | 3 +- libevmjit/Cache.cpp | 1 - libevmjit/Common.h | 37 +----------------- libevmjit/ExecutionContext.h | 73 ++++++++++++++++++++++++++++++++++- libevmjit/ExecutionEngine.cpp | 41 ++++++++++++++++++-- libevmjit/ExecutionEngine.h | 5 +-- libevmjit/GasMeter.h | 1 + libevmjit/Instruction.cpp | 5 +-- libevmjit/Instruction.h | 7 +--- libevmjit/JIT.cpp | 46 ---------------------- libevmjit/RuntimeData.h | 63 ------------------------------ libevmjit/RuntimeManager.h | 3 +- libevmjit/Type.h | 3 +- libevmjit/interface.cpp | 1 - 19 files changed, 138 insertions(+), 176 deletions(-) delete mode 100644 libevmjit/JIT.cpp delete mode 100644 libevmjit/RuntimeData.h diff --git a/include/evmjit/DataTypes.h b/include/evmjit/DataTypes.h index 179d9a372..6f06a81d6 100644 --- a/include/evmjit/DataTypes.h +++ b/include/evmjit/DataTypes.h @@ -3,11 +3,23 @@ #include #include +#ifdef _MSC_VER +#define EXPORT __declspec(dllexport) +#define _ALLOW_KEYWORD_MACROS +#define noexcept throw() +#else +#define EXPORT +#endif + namespace dev { namespace evmjit { +using byte = uint8_t; +using bytes_ref = std::tuple; +using code_iterator = byte const*; + struct h256 { uint64_t words[4]; diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index ba7a0dbc0..5972bfa88 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -16,12 +16,6 @@ public: /// In this case the code can be executed without overhead. /// \param _codeHash The Keccak hash of the EVM code. static bool isCodeReady(h256 _codeHash); - -private: - friend class ExecutionEngine; - - static void* getCode(h256 _codeHash); - static void mapCode(h256 _codeHash, void* _funcAddr); }; } diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index a6f6f1d52..4fccca7df 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -17,7 +17,7 @@ private: friend class VMFactory; explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} - jit::RuntimeData m_data; + evmjit::RuntimeData m_data; evmjit::ExecutionContext m_context; std::unique_ptr m_fallbackVM; ///< VM used in case of input data rejected by JIT }; diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index 6c1f8f160..9d6494730 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -8,6 +8,7 @@ #include #include "preprocessor/llvm_includes_end.h" +#include "evmjit/DataTypes.h" #include "Type.h" #include "Endianness.h" #include "Utils.h" diff --git a/libevmjit/BasicBlock.h b/libevmjit/BasicBlock.h index 5e19235a7..9960a0d1c 100644 --- a/libevmjit/BasicBlock.h +++ b/libevmjit/BasicBlock.h @@ -2,7 +2,8 @@ #include -#include "Common.h" + +#include "evmjit/DataTypes.h" #include "Stack.h" namespace dev @@ -13,6 +14,7 @@ namespace jit { using instr_idx = uint64_t; +using namespace evmjit; class BasicBlock { diff --git a/libevmjit/CMakeLists.txt b/libevmjit/CMakeLists.txt index 324ad6717..67e5f5800 100644 --- a/libevmjit/CMakeLists.txt +++ b/libevmjit/CMakeLists.txt @@ -17,10 +17,9 @@ set(SOURCES GasMeter.cpp GasMeter.h Instruction.cpp Instruction.h interface.cpp interface.h - JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h + ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h Memory.cpp Memory.h Optimizer.cpp Optimizer.h - RuntimeData.h RuntimeManager.cpp RuntimeManager.h Stack.cpp Stack.h Type.cpp Type.h diff --git a/libevmjit/Cache.cpp b/libevmjit/Cache.cpp index 21be82d11..e64cb5430 100644 --- a/libevmjit/Cache.cpp +++ b/libevmjit/Cache.cpp @@ -18,7 +18,6 @@ namespace dev { namespace evmjit { - using namespace eth::jit; namespace { diff --git a/libevmjit/Common.h b/libevmjit/Common.h index e99308174..5067f4fb1 100644 --- a/libevmjit/Common.h +++ b/libevmjit/Common.h @@ -3,48 +3,13 @@ #include #include -#ifdef _MSC_VER -#define EXPORT __declspec(dllexport) -#define _ALLOW_KEYWORD_MACROS -#define noexcept throw() -#else -#define EXPORT -#endif - namespace dev { namespace eth { -namespace jit -{ - -using byte = uint8_t; -using bytes_ref = std::tuple; -using code_iterator = byte const*; - -enum class ReturnCode -{ - // Success codes - Stop = 0, - Return = 1, - Suicide = 2, - - // Standard error codes - OutOfGas = -1, - StackUnderflow = -2, - BadJumpDestination = -3, - BadInstruction = -4, - Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected - - // Internal error codes - LLVMConfigError = -101, - LLVMCompileError = -102, - LLVMLinkError = -103, +namespace jit{ - UnexpectedException = -111, - LinkerWorkaround = -299, -}; #define UNTESTED assert(false) diff --git a/libevmjit/ExecutionContext.h b/libevmjit/ExecutionContext.h index 6d40e2819..fd7e8f11a 100644 --- a/libevmjit/ExecutionContext.h +++ b/libevmjit/ExecutionContext.h @@ -1,12 +1,81 @@ #pragma once -#include "RuntimeData.h" +#include "evmjit/DataTypes.h" namespace dev { namespace evmjit { - using namespace eth::jit; // FIXME + +struct RuntimeData +{ + enum Index + { + Gas, + GasPrice, + CallData, + CallDataSize, + Address, + Caller, + Origin, + CallValue, + CoinBase, + Difficulty, + GasLimit, + Number, + Timestamp, + Code, + CodeSize, + + SuicideDestAddress = Address, ///< Suicide balance destination address + ReturnData = CallData, ///< Return data pointer (set only in case of RETURN) + ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN) + }; + + int64_t gas = 0; + int64_t gasPrice = 0; + byte const* callData = nullptr; + uint64_t callDataSize = 0; + i256 address; + i256 caller; + i256 origin; + i256 callValue; + i256 coinBase; + i256 difficulty; + i256 gasLimit; + uint64_t number = 0; + int64_t timestamp = 0; + byte const* code = nullptr; + uint64_t codeSize = 0; + h256 codeHash; +}; + +/// VM Environment (ExtVM) opaque type +struct Env; + +enum class ReturnCode +{ + // Success codes + Stop = 0, + Return = 1, + Suicide = 2, + + // Standard error codes + OutOfGas = -1, + StackUnderflow = -2, + BadJumpDestination = -3, + BadInstruction = -4, + Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected + + // Internal error codes + LLVMConfigError = -101, + LLVMCompileError = -102, + LLVMLinkError = -103, + + UnexpectedException = -111, + + LinkerWorkaround = -299, +}; class ExecutionContext { diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index e424d9d3a..79ae1315c 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -32,6 +32,7 @@ namespace dev { namespace evmjit { +using namespace eth::jit; namespace { @@ -53,7 +54,7 @@ std::string hash2str(i256 const& _hash) return str; } -void printVersion() // FIXME: Fix LLVM version parsing +void printVersion() { std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n" << " EVMJIT version " << EVMJIT_VERSION << "\n" @@ -124,6 +125,40 @@ std::unique_ptr init() return ee; } +class JITImpl +{ +public: + std::unordered_map codeMap; + + static JITImpl& instance() + { + static JITImpl s_instance; + return s_instance; + } + + static void* getCode(h256 _codeHash); + static void mapCode(h256 _codeHash, void* _funcAddr); +}; + +void* JITImpl::getCode(h256 _codeHash) +{ + auto& codeMap = JITImpl::instance().codeMap; + auto it = codeMap.find(_codeHash); + if (it != codeMap.end()) + return it->second; + return nullptr; +} + +void JITImpl::mapCode(h256 _codeHash, void* _funcAddr) +{ + JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr)); +} + +} // anonymous namespace + +bool JIT::isCodeReady(h256 _codeHash) +{ + return JITImpl::instance().codeMap.count(_codeHash) != 0; } ReturnCode ExecutionEngine::run(ExecutionContext& _context) @@ -142,7 +177,7 @@ ReturnCode ExecutionEngine::run(ExecutionContext& _context) auto mainFuncName = hash2str(codeHash); // TODO: Remove cast - auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(codeHash); + auto entryFuncPtr = (EntryFuncPtr) JITImpl::getCode(codeHash); if (!entryFuncPtr) { auto module = Cache::getObject(mainFuncName); @@ -168,7 +203,7 @@ ReturnCode ExecutionEngine::run(ExecutionContext& _context) entryFuncPtr = (EntryFuncPtr)s_ee->getFunctionAddress(mainFuncName); if (!CHECK(entryFuncPtr)) return ReturnCode::LLVMLinkError; - JIT::mapCode(codeHash, (void*)entryFuncPtr); // FIXME: Remove cast + JITImpl::mapCode(codeHash, (void*)entryFuncPtr); // FIXME: Remove cast } listener->stateChanged(ExecState::Execution); diff --git a/libevmjit/ExecutionEngine.h b/libevmjit/ExecutionEngine.h index d14e50702..04c3218b0 100644 --- a/libevmjit/ExecutionEngine.h +++ b/libevmjit/ExecutionEngine.h @@ -1,14 +1,13 @@ #pragma once -#include "Common.h" +#include "evmjit/DataTypes.h" namespace dev { namespace evmjit { class ExecutionContext; - -using namespace eth::jit; +enum class ReturnCode; enum class ExecState { diff --git a/libevmjit/GasMeter.h b/libevmjit/GasMeter.h index aecc07315..93a6cc24c 100644 --- a/libevmjit/GasMeter.h +++ b/libevmjit/GasMeter.h @@ -10,6 +10,7 @@ namespace eth namespace jit { class RuntimeManager; +using namespace evmjit; class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper { diff --git a/libevmjit/Instruction.cpp b/libevmjit/Instruction.cpp index f70b020f8..c2e267cdb 100644 --- a/libevmjit/Instruction.cpp +++ b/libevmjit/Instruction.cpp @@ -6,9 +6,7 @@ namespace dev { -namespace eth -{ -namespace jit +namespace evmjit { llvm::APInt readPushData(code_iterator& _curr, code_iterator _end) @@ -39,4 +37,3 @@ void skipPushData(code_iterator& _curr, code_iterator _end) } } -} diff --git a/libevmjit/Instruction.h b/libevmjit/Instruction.h index 6785213d6..2da3c4c65 100644 --- a/libevmjit/Instruction.h +++ b/libevmjit/Instruction.h @@ -1,6 +1,6 @@ #pragma once -#include "Common.h" +#include "evmjit/DataTypes.h" namespace llvm { @@ -9,9 +9,7 @@ namespace llvm namespace dev { -namespace eth -{ -namespace jit +namespace evmjit { /// Virtual machine bytecode instruction. @@ -236,4 +234,3 @@ void skipPushData(code_iterator& _curr, code_iterator _end); } } -} diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp deleted file mode 100644 index 9774c7396..000000000 --- a/libevmjit/JIT.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "evmjit/JIT.h" - -#include - -namespace dev -{ -namespace evmjit -{ -namespace -{ - -class JITImpl: JIT -{ -public: - std::unordered_map codeMap; - - static JITImpl& instance() - { - static JITImpl s_instance; - return s_instance; - } -}; - -} // anonymous namespace - -bool JIT::isCodeReady(h256 _codeHash) -{ - return JITImpl::instance().codeMap.count(_codeHash) != 0; -} - -void* JIT::getCode(h256 _codeHash) -{ - auto& codeMap = JITImpl::instance().codeMap; - auto it = codeMap.find(_codeHash); - if (it != codeMap.end()) - return it->second; - return nullptr; -} - -void JIT::mapCode(h256 _codeHash, void* _funcAddr) -{ - JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr)); -} - -} -} diff --git a/libevmjit/RuntimeData.h b/libevmjit/RuntimeData.h deleted file mode 100644 index 6a5cd0d14..000000000 --- a/libevmjit/RuntimeData.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "evmjit/DataTypes.h" -#include "Common.h" - -namespace dev -{ -namespace eth -{ -namespace jit -{ -using evmjit::i256; -using evmjit::h256; - -struct RuntimeData -{ - enum Index - { - Gas, - GasPrice, - CallData, - CallDataSize, - Address, - Caller, - Origin, - CallValue, - CoinBase, - Difficulty, - GasLimit, - Number, - Timestamp, - Code, - CodeSize, - - SuicideDestAddress = Address, ///< Suicide balance destination address - ReturnData = CallData, ///< Return data pointer (set only in case of RETURN) - ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN) - }; - - int64_t gas = 0; - int64_t gasPrice = 0; - byte const* callData = nullptr; - uint64_t callDataSize = 0; - i256 address; - i256 caller; - i256 origin; - i256 callValue; - i256 coinBase; - i256 difficulty; - i256 gasLimit; - uint64_t number = 0; - int64_t timestamp = 0; - byte const* code = nullptr; - uint64_t codeSize = 0; - h256 codeHash; -}; - -/// VM Environment (ExtVM) opaque type -struct Env; - -} -} -} diff --git a/libevmjit/RuntimeManager.h b/libevmjit/RuntimeManager.h index 4c36c4ef3..088611093 100644 --- a/libevmjit/RuntimeManager.h +++ b/libevmjit/RuntimeManager.h @@ -1,8 +1,8 @@ #pragma once +#include "ExecutionContext.h" #include "CompilerHelper.h" #include "Type.h" -#include "RuntimeData.h" #include "Instruction.h" namespace dev @@ -11,6 +11,7 @@ namespace eth { namespace jit { +using namespace evmjit; class Stack; class RuntimeManager: public CompilerHelper diff --git a/libevmjit/Type.h b/libevmjit/Type.h index ba9181f6b..b1b6d1ad4 100644 --- a/libevmjit/Type.h +++ b/libevmjit/Type.h @@ -6,7 +6,7 @@ #include #include "preprocessor/llvm_includes_end.h" // FIXME: LLVM 3.7: check if needed -#include "Common.h" +#include "ExecutionContext.h" // FIXME: crappy dependence namespace dev { @@ -14,6 +14,7 @@ namespace eth { namespace jit { +using namespace evmjit; struct Type { diff --git a/libevmjit/interface.cpp b/libevmjit/interface.cpp index 3b4145cb7..4aef5995f 100644 --- a/libevmjit/interface.cpp +++ b/libevmjit/interface.cpp @@ -3,7 +3,6 @@ extern "C" { -using namespace dev::eth::jit; using namespace dev::evmjit; EXPORT void* evmjit_create(RuntimeData* _data, Env* _env) noexcept From 9cc8c7895e6189065a6547e78c65df5130c5e993 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 18 May 2015 10:25:35 +0200 Subject: [PATCH 33/72] Code migration. --- include/evmjit/JIT.h | 4 ++ libevmjit-cpp/JitVM.cpp | 3 +- libevmjit-cpp/JitVM.h | 1 - libevmjit/CMakeLists.txt | 3 +- libevmjit/Cache.cpp | 6 +-- libevmjit/Cache.h | 4 +- libevmjit/ExecStats.h | 31 +++++++++++++-- libevmjit/ExecutionEngine.h | 46 ---------------------- libevmjit/{ExecutionEngine.cpp => JIT.cpp} | 14 +------ libevmjit/interface.cpp | 4 +- 10 files changed, 44 insertions(+), 72 deletions(-) delete mode 100644 libevmjit/ExecutionEngine.h rename libevmjit/{ExecutionEngine.cpp => JIT.cpp} (94%) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index 5972bfa88..f4bb9d159 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -6,6 +6,8 @@ namespace dev { namespace evmjit { +enum class ReturnCode; +class ExecutionContext; class JIT { @@ -16,6 +18,8 @@ public: /// In this case the code can be executed without overhead. /// \param _codeHash The Keccak hash of the EVM code. static bool isCodeReady(h256 _codeHash); + + EXPORT static ReturnCode exec(ExecutionContext& _context); }; } diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index a4eed5793..d40aa2b1c 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include "Utils.h" @@ -53,7 +54,7 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) m_data.codeHash = eth2llvm(sha3(_ext.code)); m_context.init(m_data, reinterpret_cast(&_ext)); - auto exitCode = evmjit::ExecutionEngine::run(m_context); + auto exitCode = evmjit::JIT::exec(m_context); switch (exitCode) { case evmjit::ReturnCode::Suicide: diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 4fccca7df..5bba7b5f0 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -2,7 +2,6 @@ #include #include -#include namespace dev { diff --git a/libevmjit/CMakeLists.txt b/libevmjit/CMakeLists.txt index 67e5f5800..525030285 100644 --- a/libevmjit/CMakeLists.txt +++ b/libevmjit/CMakeLists.txt @@ -12,12 +12,11 @@ set(SOURCES Endianness.cpp Endianness.h ExecStats.cpp ExecStats.h ExecutionContext.cpp ExecutionContext.h - ExecutionEngine.cpp ExecutionEngine.h Ext.cpp Ext.h GasMeter.cpp GasMeter.h Instruction.cpp Instruction.h interface.cpp interface.h - ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h + JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h Memory.cpp Memory.h Optimizer.cpp Optimizer.h RuntimeManager.cpp RuntimeManager.h diff --git a/libevmjit/Cache.cpp b/libevmjit/Cache.cpp index e64cb5430..0e19b6276 100644 --- a/libevmjit/Cache.cpp +++ b/libevmjit/Cache.cpp @@ -10,7 +10,7 @@ #include #include "preprocessor/llvm_includes_end.h" -#include "ExecutionEngine.h" +#include "ExecStats.h" #include "Utils.h" #include "BuildInfo.gen.h" @@ -23,7 +23,7 @@ namespace { CacheMode g_mode; std::unique_ptr g_lastObject; - ExecutionEngineListener* g_listener; + JITListener* g_listener; static const size_t c_versionStampLength = 32; llvm::StringRef getLibVersionStamp() @@ -38,7 +38,7 @@ namespace } } -ObjectCache* Cache::init(CacheMode _mode, ExecutionEngineListener* _listener) +ObjectCache* Cache::init(CacheMode _mode, JITListener* _listener) { g_mode = _mode; g_listener = _listener; diff --git a/libevmjit/Cache.h b/libevmjit/Cache.h index 61635b8a1..2f152c9f8 100644 --- a/libevmjit/Cache.h +++ b/libevmjit/Cache.h @@ -14,7 +14,7 @@ namespace dev { namespace evmjit { -class ExecutionEngineListener; +class JITListener; enum class CacheMode { @@ -43,7 +43,7 @@ public: class Cache { public: - static ObjectCache* init(CacheMode _mode, ExecutionEngineListener* _listener); + static ObjectCache* init(CacheMode _mode, JITListener* _listener); static std::unique_ptr getObject(std::string const& id); /// Clears cache storage diff --git a/libevmjit/ExecStats.h b/libevmjit/ExecStats.h index 3ea77733b..4a5ae00e1 100644 --- a/libevmjit/ExecStats.h +++ b/libevmjit/ExecStats.h @@ -5,14 +5,39 @@ #include #include -#include "ExecutionEngine.h" - namespace dev { namespace evmjit { -class ExecStats : public ExecutionEngineListener +enum class ExecState +{ + Started, + CacheLoad, + CacheWrite, + Compilation, + Optimization, + CodeGen, + Execution, + Return, + Finished +}; + +class JITListener +{ +public: + JITListener() = default; + JITListener(JITListener const&) = delete; + JITListener& operator=(JITListener) = delete; + virtual ~JITListener() {} + + virtual void executionStarted() {} + virtual void executionEnded() {} + + virtual void stateChanged(ExecState) {} +}; + +class ExecStats : public JITListener { public: using clock = std::chrono::high_resolution_clock; diff --git a/libevmjit/ExecutionEngine.h b/libevmjit/ExecutionEngine.h deleted file mode 100644 index 04c3218b0..000000000 --- a/libevmjit/ExecutionEngine.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "evmjit/DataTypes.h" - -namespace dev -{ -namespace evmjit -{ -class ExecutionContext; -enum class ReturnCode; - -enum class ExecState -{ - Started, - CacheLoad, - CacheWrite, - Compilation, - Optimization, - CodeGen, - Execution, - Return, - Finished -}; - -class ExecutionEngineListener -{ -public: - ExecutionEngineListener() = default; - ExecutionEngineListener(ExecutionEngineListener const&) = delete; - ExecutionEngineListener& operator=(ExecutionEngineListener) = delete; - virtual ~ExecutionEngineListener() {} - - virtual void executionStarted() {} - virtual void executionEnded() {} - - virtual void stateChanged(ExecState) {} -}; - -class ExecutionEngine -{ -public: - EXPORT static ReturnCode run(ExecutionContext& _context); -}; - -} -} diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/JIT.cpp similarity index 94% rename from libevmjit/ExecutionEngine.cpp rename to libevmjit/JIT.cpp index 79ae1315c..6fc44e5eb 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/JIT.cpp @@ -1,17 +1,8 @@ -#include "ExecutionEngine.h" - -#include -#include -#include -#include -#include -#include +#include "evmjit/JIT.h" #include "preprocessor/llvm_includes_start.h" #include #include -#include -#include #include #include #include @@ -20,7 +11,6 @@ #include "preprocessor/llvm_includes_end.h" #include "ExecutionContext.h" -#include "evmjit/JIT.h" #include "Compiler.h" #include "Optimizer.h" #include "Cache.h" @@ -161,7 +151,7 @@ bool JIT::isCodeReady(h256 _codeHash) return JITImpl::instance().codeMap.count(_codeHash) != 0; } -ReturnCode ExecutionEngine::run(ExecutionContext& _context) +ReturnCode JIT::exec(ExecutionContext& _context) { static auto s_ee = init(); diff --git a/libevmjit/interface.cpp b/libevmjit/interface.cpp index 4aef5995f..34491e772 100644 --- a/libevmjit/interface.cpp +++ b/libevmjit/interface.cpp @@ -1,4 +1,4 @@ -#include "ExecutionEngine.h" +#include "evmjit/JIT.h" #include "ExecutionContext.h" extern "C" @@ -23,7 +23,7 @@ EXPORT int evmjit_run(ExecutionContext* _context) noexcept { try { - auto returnCode = ExecutionEngine::run(*_context); + auto returnCode = JIT::exec(*_context); return static_cast(returnCode); } catch(...) From 8fe2b1a3d66a007b2dbce9861f7797ec67028eac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 18 May 2015 11:35:05 +0200 Subject: [PATCH 34/72] JIT implementation cleanups. --- include/evmjit/JIT.h | 2 +- libevmjit/JIT.cpp | 80 ++++++++++++++++++++++---------------------- 2 files changed, 41 insertions(+), 41 deletions(-) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index f4bb9d159..58c19b426 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -17,7 +17,7 @@ public: /// Returns `true` if the EVM code has been compiled and loaded into memory. /// In this case the code can be executed without overhead. /// \param _codeHash The Keccak hash of the EVM code. - static bool isCodeReady(h256 _codeHash); + static bool isCodeReady(h256 const& _codeHash); EXPORT static ReturnCode exec(ExecutionContext& _context); }; diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp index 6fc44e5eb..c4ce24ee2 100644 --- a/libevmjit/JIT.cpp +++ b/libevmjit/JIT.cpp @@ -26,7 +26,7 @@ using namespace eth::jit; namespace { -using EntryFuncPtr = ReturnCode(*)(ExecutionContext*); +using ExecFunc = ReturnCode(*)(ExecutionContext*); std::string hash2str(i256 const& _hash) { @@ -78,10 +78,28 @@ void parseOptions() cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); } -std::unique_ptr init() +class JITImpl { - /// ExecutionEngine is created only once + std::unique_ptr m_engine; + std::unordered_map m_codeMap; +public: + static JITImpl& instance() + { + static JITImpl s_instance; + return s_instance; + } + + JITImpl(); + + llvm::ExecutionEngine& engine() { return *m_engine; } + + ExecFunc getExecFunc(h256 const& _codeHash) const; + void mapExecFunc(h256 _codeHash, ExecFunc _funcAddr); +}; + +JITImpl::JITImpl() +{ parseOptions(); bool preloadCache = g_cache == CacheMode::preload; @@ -103,57 +121,39 @@ std::unique_ptr init() builder.setEngineKind(llvm::EngineKind::JIT); builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); - auto ee = std::unique_ptr{builder.create()}; + m_engine.reset(builder.create()); // TODO: Update cache listener - ee->setObjectCache(Cache::init(g_cache, nullptr)); + m_engine->setObjectCache(Cache::init(g_cache, nullptr)); // FIXME: Disabled during API changes //if (preloadCache) - // Cache::preload(*ee, funcCache); - - return ee; + // Cache::preload(*m_engine, funcCache); } -class JITImpl -{ -public: - std::unordered_map codeMap; - - static JITImpl& instance() - { - static JITImpl s_instance; - return s_instance; - } - - static void* getCode(h256 _codeHash); - static void mapCode(h256 _codeHash, void* _funcAddr); -}; - -void* JITImpl::getCode(h256 _codeHash) +ExecFunc JITImpl::getExecFunc(h256 const& _codeHash) const { - auto& codeMap = JITImpl::instance().codeMap; - auto it = codeMap.find(_codeHash); - if (it != codeMap.end()) + auto it = m_codeMap.find(_codeHash); + if (it != m_codeMap.end()) return it->second; return nullptr; } -void JITImpl::mapCode(h256 _codeHash, void* _funcAddr) +void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _funcAddr) { - JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr)); + m_codeMap.emplace(std::move(_codeHash), _funcAddr); } } // anonymous namespace -bool JIT::isCodeReady(h256 _codeHash) +bool JIT::isCodeReady(h256 const& _codeHash) { - return JITImpl::instance().codeMap.count(_codeHash) != 0; + return JITImpl::instance().getExecFunc(_codeHash) != nullptr; } ReturnCode JIT::exec(ExecutionContext& _context) { - static auto s_ee = init(); + auto& jit = JITImpl::instance(); std::unique_ptr listener{new ExecStats}; listener->stateChanged(ExecState::Started); @@ -167,8 +167,8 @@ ReturnCode JIT::exec(ExecutionContext& _context) auto mainFuncName = hash2str(codeHash); // TODO: Remove cast - auto entryFuncPtr = (EntryFuncPtr) JITImpl::getCode(codeHash); - if (!entryFuncPtr) + auto execFunc = jit.getExecFunc(codeHash); + if (!execFunc) { auto module = Cache::getObject(mainFuncName); if (!module) @@ -188,20 +188,20 @@ ReturnCode JIT::exec(ExecutionContext& _context) if (g_dump) module->dump(); - s_ee->addModule(std::move(module)); + jit.engine().addModule(std::move(module)); listener->stateChanged(ExecState::CodeGen); - entryFuncPtr = (EntryFuncPtr)s_ee->getFunctionAddress(mainFuncName); - if (!CHECK(entryFuncPtr)) + execFunc = (ExecFunc)jit.engine().getFunctionAddress(mainFuncName); + if (!CHECK(execFunc)) return ReturnCode::LLVMLinkError; - JITImpl::mapCode(codeHash, (void*)entryFuncPtr); // FIXME: Remove cast + jit.mapExecFunc(codeHash, execFunc); } listener->stateChanged(ExecState::Execution); - auto returnCode = entryFuncPtr(&_context); + auto returnCode = execFunc(&_context); listener->stateChanged(ExecState::Return); if (returnCode == ReturnCode::Return) - _context.returnData = _context.getReturnData(); // Save reference to return data + _context.returnData = _context.getReturnData(); // Save reference to return data listener->stateChanged(ExecState::Finished); From 5f4557eae281864b7df68b412adb93d2f3403231 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 18 May 2015 13:59:16 +0200 Subject: [PATCH 35/72] EVM JIT C++ interface in one header. --- include/evmjit/DataTypes.h | 68 -------------- include/evmjit/JIT.h | 160 ++++++++++++++++++++++++++++++++- libevmjit-cpp/Env.cpp | 1 - libevmjit-cpp/JitVM.cpp | 1 - libevmjit-cpp/JitVM.h | 2 +- libevmjit-cpp/Utils.h | 2 +- libevmjit/Arith256.cpp | 1 - libevmjit/BasicBlock.h | 6 +- libevmjit/CMakeLists.txt | 6 +- libevmjit/Common.h | 8 +- libevmjit/Compiler.h | 1 - libevmjit/ExecutionContext.cpp | 33 ------- libevmjit/ExecutionContext.h | 110 ----------------------- libevmjit/Instruction.h | 2 +- libevmjit/JIT.cpp | 25 +++++- libevmjit/RuntimeManager.h | 1 - libevmjit/Type.h | 2 +- libevmjit/interface.cpp | 1 - 18 files changed, 192 insertions(+), 238 deletions(-) delete mode 100644 include/evmjit/DataTypes.h delete mode 100644 libevmjit/ExecutionContext.cpp delete mode 100644 libevmjit/ExecutionContext.h diff --git a/include/evmjit/DataTypes.h b/include/evmjit/DataTypes.h deleted file mode 100644 index 6f06a81d6..000000000 --- a/include/evmjit/DataTypes.h +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include -#include - -#ifdef _MSC_VER -#define EXPORT __declspec(dllexport) -#define _ALLOW_KEYWORD_MACROS -#define noexcept throw() -#else -#define EXPORT -#endif - -namespace dev -{ -namespace evmjit -{ - -using byte = uint8_t; -using bytes_ref = std::tuple; -using code_iterator = byte const*; - -struct h256 -{ - uint64_t words[4]; -}; - -inline bool operator==(h256 _h1, h256 _h2) -{ - return _h1.words[0] == _h2.words[0] && - _h1.words[1] == _h2.words[1] && - _h1.words[2] == _h2.words[2] && - _h1.words[3] == _h2.words[3]; -} - -/// Representation of 256-bit value binary compatible with LLVM i256 -struct i256 -{ - uint64_t a = 0; - uint64_t b = 0; - uint64_t c = 0; - uint64_t d = 0; - - i256() = default; - i256(h256 _h) - { - a = _h.words[0]; - b = _h.words[1]; - c = _h.words[2]; - d = _h.words[3]; - } -}; - -} -} - -namespace std -{ -template<> struct hash -{ - size_t operator()(dev::evmjit::h256 const& _h) const - { - /// This implementation expects the argument to be a full 256-bit Keccak hash. - /// It does nothing more than returning a slice of the input hash. - return static_cast(_h.words[0]); - }; -}; -} diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index 58c19b426..cbf0e6e78 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -1,13 +1,153 @@ #pragma once -#include "evmjit/DataTypes.h" +#include +#include + +#ifdef _MSC_VER +#define EXPORT __declspec(dllexport) +#define _ALLOW_KEYWORD_MACROS +#define noexcept throw() +#else +#define EXPORT +#endif namespace dev { namespace evmjit { -enum class ReturnCode; -class ExecutionContext; + +using byte = uint8_t; +using bytes_ref = std::tuple; + +struct h256 +{ + uint64_t words[4]; +}; + +inline bool operator==(h256 _h1, h256 _h2) +{ + return _h1.words[0] == _h2.words[0] && + _h1.words[1] == _h2.words[1] && + _h1.words[2] == _h2.words[2] && + _h1.words[3] == _h2.words[3]; +} + +/// Representation of 256-bit value binary compatible with LLVM i256 +struct i256 +{ + uint64_t a = 0; + uint64_t b = 0; + uint64_t c = 0; + uint64_t d = 0; + + i256() = default; + i256(h256 _h) + { + a = _h.words[0]; + b = _h.words[1]; + c = _h.words[2]; + d = _h.words[3]; + } +}; + +struct RuntimeData +{ + enum Index + { + Gas, + GasPrice, + CallData, + CallDataSize, + Address, + Caller, + Origin, + CallValue, + CoinBase, + Difficulty, + GasLimit, + Number, + Timestamp, + Code, + CodeSize, + + SuicideDestAddress = Address, ///< Suicide balance destination address + ReturnData = CallData, ///< Return data pointer (set only in case of RETURN) + ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN) + }; + + int64_t gas = 0; + int64_t gasPrice = 0; + byte const* callData = nullptr; + uint64_t callDataSize = 0; + i256 address; + i256 caller; + i256 origin; + i256 callValue; + i256 coinBase; + i256 difficulty; + i256 gasLimit; + uint64_t number = 0; + int64_t timestamp = 0; + byte const* code = nullptr; + uint64_t codeSize = 0; + h256 codeHash; +}; + +/// VM Environment (ExtVM) opaque type +struct Env; + +enum class ReturnCode +{ + // Success codes + Stop = 0, + Return = 1, + Suicide = 2, + + // Standard error codes + OutOfGas = -1, + StackUnderflow = -2, + BadJumpDestination = -3, + BadInstruction = -4, + Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected + + // Internal error codes + LLVMConfigError = -101, + LLVMCompileError = -102, + LLVMLinkError = -103, + + UnexpectedException = -111, + + LinkerWorkaround = -299, +}; + +class ExecutionContext +{ +public: + ExecutionContext() = default; + ExecutionContext(RuntimeData& _data, Env* _env) { init(_data, _env); } + ExecutionContext(ExecutionContext const&) = delete; + ExecutionContext& operator=(ExecutionContext const&) = delete; + EXPORT ~ExecutionContext(); + + void init(RuntimeData& _data, Env* _env) { m_data = &_data; m_env = _env; } + + byte const* code() const { return m_data->code; } + uint64_t codeSize() const { return m_data->codeSize; } + h256 const& codeHash() const { return m_data->codeHash; } + + bytes_ref getReturnData() const; + +private: + RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract. + Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract. + byte* m_memData = nullptr; + uint64_t m_memSize = 0; + uint64_t m_memCap = 0; + +public: + /// Reference to returned data (RETURN opcode used) + bytes_ref returnData; +}; class JIT { @@ -24,3 +164,17 @@ public: } } + +namespace std +{ +template<> struct hash +{ + size_t operator()(dev::evmjit::h256 const& _h) const + { + /// This implementation expects the argument to be a full 256-bit Keccak hash. + /// It does nothing more than returning a slice of the input hash. + return static_cast(_h.words[0]); + }; +}; +} + diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index a5a60f48c..366f5704a 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "Utils.h" diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index d40aa2b1c..e9559026a 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include "Utils.h" diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 5bba7b5f0..fdc87248a 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace dev { diff --git a/libevmjit-cpp/Utils.h b/libevmjit-cpp/Utils.h index f9b9b2ef4..d52861fcc 100644 --- a/libevmjit-cpp/Utils.h +++ b/libevmjit-cpp/Utils.h @@ -1,6 +1,6 @@ #pragma once -#include +#include namespace dev { diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index 9d6494730..6c1f8f160 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -8,7 +8,6 @@ #include #include "preprocessor/llvm_includes_end.h" -#include "evmjit/DataTypes.h" #include "Type.h" #include "Endianness.h" #include "Utils.h" diff --git a/libevmjit/BasicBlock.h b/libevmjit/BasicBlock.h index 9960a0d1c..321499196 100644 --- a/libevmjit/BasicBlock.h +++ b/libevmjit/BasicBlock.h @@ -2,8 +2,7 @@ #include - -#include "evmjit/DataTypes.h" +#include "Common.h" #include "Stack.h" namespace dev @@ -12,9 +11,8 @@ namespace eth { namespace jit { - -using instr_idx = uint64_t; using namespace evmjit; +using instr_idx = uint64_t; class BasicBlock { diff --git a/libevmjit/CMakeLists.txt b/libevmjit/CMakeLists.txt index 525030285..80108e15b 100644 --- a/libevmjit/CMakeLists.txt +++ b/libevmjit/CMakeLists.txt @@ -1,22 +1,20 @@ set(TARGET_NAME evmjit) set(SOURCES + JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h Arith256.cpp Arith256.h Array.cpp Array.h BasicBlock.cpp BasicBlock.h Cache.cpp Cache.h - Common.h + Common.h Compiler.cpp Compiler.h CompilerHelper.cpp CompilerHelper.h - ${EVMJIT_INCLUDE_DIR}/evmjit/DataTypes.h Endianness.cpp Endianness.h ExecStats.cpp ExecStats.h - ExecutionContext.cpp ExecutionContext.h Ext.cpp Ext.h GasMeter.cpp GasMeter.h Instruction.cpp Instruction.h interface.cpp interface.h - JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h Memory.cpp Memory.h Optimizer.cpp Optimizer.h RuntimeManager.cpp RuntimeManager.h diff --git a/libevmjit/Common.h b/libevmjit/Common.h index 5067f4fb1..4a6d7b4f3 100644 --- a/libevmjit/Common.h +++ b/libevmjit/Common.h @@ -1,18 +1,16 @@ #pragma once -#include #include namespace dev { -namespace eth +namespace evmjit { -namespace jit{ - +using byte = uint8_t; +using code_iterator = byte const*; #define UNTESTED assert(false) } } -} diff --git a/libevmjit/Compiler.h b/libevmjit/Compiler.h index 9b9fe7160..0b31ae0f2 100644 --- a/libevmjit/Compiler.h +++ b/libevmjit/Compiler.h @@ -1,6 +1,5 @@ #pragma once -#include "Common.h" #include "BasicBlock.h" namespace dev diff --git a/libevmjit/ExecutionContext.cpp b/libevmjit/ExecutionContext.cpp deleted file mode 100644 index fab6fad87..000000000 --- a/libevmjit/ExecutionContext.cpp +++ /dev/null @@ -1,33 +0,0 @@ -#include "ExecutionContext.h" -#include - -namespace dev -{ -namespace evmjit -{ - -extern "C" void ext_free(void* _data) noexcept; - -ExecutionContext::~ExecutionContext() -{ - if (m_memData) - ext_free(m_memData); // Use helper free to check memory leaks -} - -bytes_ref ExecutionContext::getReturnData() const -{ - auto data = m_data->callData; - auto size = static_cast(m_data->callDataSize); - - if (data < m_memData || data >= m_memData + m_memSize || size == 0) - { - assert(size == 0); // data can be an invalid pointer only if size is 0 - m_data->callData = nullptr; - return {}; - } - - return bytes_ref{data, size}; -} - -} -} diff --git a/libevmjit/ExecutionContext.h b/libevmjit/ExecutionContext.h deleted file mode 100644 index fd7e8f11a..000000000 --- a/libevmjit/ExecutionContext.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include "evmjit/DataTypes.h" - -namespace dev -{ -namespace evmjit -{ - -struct RuntimeData -{ - enum Index - { - Gas, - GasPrice, - CallData, - CallDataSize, - Address, - Caller, - Origin, - CallValue, - CoinBase, - Difficulty, - GasLimit, - Number, - Timestamp, - Code, - CodeSize, - - SuicideDestAddress = Address, ///< Suicide balance destination address - ReturnData = CallData, ///< Return data pointer (set only in case of RETURN) - ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN) - }; - - int64_t gas = 0; - int64_t gasPrice = 0; - byte const* callData = nullptr; - uint64_t callDataSize = 0; - i256 address; - i256 caller; - i256 origin; - i256 callValue; - i256 coinBase; - i256 difficulty; - i256 gasLimit; - uint64_t number = 0; - int64_t timestamp = 0; - byte const* code = nullptr; - uint64_t codeSize = 0; - h256 codeHash; -}; - -/// VM Environment (ExtVM) opaque type -struct Env; - -enum class ReturnCode -{ - // Success codes - Stop = 0, - Return = 1, - Suicide = 2, - - // Standard error codes - OutOfGas = -1, - StackUnderflow = -2, - BadJumpDestination = -3, - BadInstruction = -4, - Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected - - // Internal error codes - LLVMConfigError = -101, - LLVMCompileError = -102, - LLVMLinkError = -103, - - UnexpectedException = -111, - - LinkerWorkaround = -299, -}; - -class ExecutionContext -{ -public: - ExecutionContext() = default; - ExecutionContext(RuntimeData& _data, Env* _env) { init(_data, _env); } - ExecutionContext(ExecutionContext const&) = delete; - ExecutionContext& operator=(ExecutionContext const&) = delete; - EXPORT ~ExecutionContext(); - - void init(RuntimeData& _data, Env* _env) { m_data = &_data; m_env = _env; } - - byte const* code() const { return m_data->code; } - uint64_t codeSize() const { return m_data->codeSize; } - h256 const& codeHash() const { return m_data->codeHash; } - - bytes_ref getReturnData() const; - -private: - RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract. - Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract. - byte* m_memData = nullptr; - uint64_t m_memSize = 0; - uint64_t m_memCap = 0; - -public: - /// Reference to returned data (RETURN opcode used) - bytes_ref returnData; -}; - -} -} diff --git a/libevmjit/Instruction.h b/libevmjit/Instruction.h index 2da3c4c65..89add0958 100644 --- a/libevmjit/Instruction.h +++ b/libevmjit/Instruction.h @@ -1,6 +1,6 @@ #pragma once -#include "evmjit/DataTypes.h" +#include "Common.h" namespace llvm { diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp index c4ce24ee2..f8b4d4c00 100644 --- a/libevmjit/JIT.cpp +++ b/libevmjit/JIT.cpp @@ -10,7 +10,6 @@ #include #include "preprocessor/llvm_includes_end.h" -#include "ExecutionContext.h" #include "Compiler.h" #include "Optimizer.h" #include "Cache.h" @@ -211,5 +210,29 @@ ReturnCode JIT::exec(ExecutionContext& _context) return returnCode; } + +extern "C" void ext_free(void* _data) noexcept; + +ExecutionContext::~ExecutionContext() +{ + if (m_memData) + ext_free(m_memData); // Use helper free to check memory leaks +} + +bytes_ref ExecutionContext::getReturnData() const +{ + auto data = m_data->callData; + auto size = static_cast(m_data->callDataSize); + + if (data < m_memData || data >= m_memData + m_memSize || size == 0) + { + assert(size == 0); // data can be an invalid pointer only if size is 0 + m_data->callData = nullptr; + return {}; + } + + return bytes_ref{data, size}; +} + } } diff --git a/libevmjit/RuntimeManager.h b/libevmjit/RuntimeManager.h index 088611093..5ce749933 100644 --- a/libevmjit/RuntimeManager.h +++ b/libevmjit/RuntimeManager.h @@ -1,6 +1,5 @@ #pragma once -#include "ExecutionContext.h" #include "CompilerHelper.h" #include "Type.h" #include "Instruction.h" diff --git a/libevmjit/Type.h b/libevmjit/Type.h index b1b6d1ad4..b54d101cb 100644 --- a/libevmjit/Type.h +++ b/libevmjit/Type.h @@ -6,7 +6,7 @@ #include #include "preprocessor/llvm_includes_end.h" // FIXME: LLVM 3.7: check if needed -#include "ExecutionContext.h" // FIXME: crappy dependence +#include "evmjit/JIT.h" // ReturnCode namespace dev { diff --git a/libevmjit/interface.cpp b/libevmjit/interface.cpp index 34491e772..7136f6798 100644 --- a/libevmjit/interface.cpp +++ b/libevmjit/interface.cpp @@ -1,5 +1,4 @@ #include "evmjit/JIT.h" -#include "ExecutionContext.h" extern "C" { From b56cef3b1e69cf506bc9da085caea7c42d044e9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 18 May 2015 14:35:32 +0200 Subject: [PATCH 36/72] Better eth <-> jit integer conversions. --- include/evmjit/JIT.h | 14 +++----------- libevmjit-cpp/Env.cpp | 16 ++++++++-------- libevmjit-cpp/JitVM.cpp | 18 +++++++++--------- libevmjit-cpp/Utils.h | 27 ++++++++++++++------------- 4 files changed, 34 insertions(+), 41 deletions(-) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index cbf0e6e78..c12637a25 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -35,21 +35,13 @@ inline bool operator==(h256 _h1, h256 _h2) /// Representation of 256-bit value binary compatible with LLVM i256 struct i256 { - uint64_t a = 0; - uint64_t b = 0; - uint64_t c = 0; - uint64_t d = 0; + uint64_t words[4] = {0,}; i256() = default; - i256(h256 _h) - { - a = _h.words[0]; - b = _h.words[1]; - c = _h.words[2]; - d = _h.words[3]; - } + i256(h256 _h) { *this = *reinterpret_cast(&_h); } }; +// TODO: Merge with ExecutionContext struct RuntimeData { enum Index diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index 366f5704a..b2382b8ba 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -20,15 +20,15 @@ extern "C" EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) { - auto index = llvm2eth(*_index); + auto index = jit2eth(*_index); auto value = _env->store(index); // Interface uses native endianness - *o_value = eth2llvm(value); + *o_value = eth2jit(value); } EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value) { - auto index = llvm2eth(*_index); - auto value = llvm2eth(*_value); + auto index = jit2eth(*_index); + auto value = jit2eth(*_value); if (value == 0 && _env->store(index) != 0) // If delete _env->sub.refunds += c_sstoreRefundGas; // Increase refund counter @@ -39,17 +39,17 @@ extern "C" EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value) { auto u = _env->balance(right160(*_address)); - *o_value = eth2llvm(u); + *o_value = eth2jit(u); } EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash) { - *o_hash = _env->blockhash(llvm2eth(*_number)); + *o_hash = _env->blockhash(jit2eth(*_number)); } EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address) { - auto endowment = llvm2eth(*_endowment); + auto endowment = jit2eth(*_endowment); if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) { u256 gas = *io_gas; @@ -63,7 +63,7 @@ extern "C" EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) { - auto value = llvm2eth(*_value); + auto value = jit2eth(*_value); auto receiveAddress = right160(*_receiveAddress); auto codeAddress = right160(*_codeAddress); const auto isCall = receiveAddress == codeAddress; // OPT: The same address pointer can be used if not CODECALL diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index e9559026a..3bdbc37b8 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -39,25 +39,25 @@ bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) m_data.gasPrice = static_cast(_ext.gasPrice); m_data.callData = _ext.data.data(); m_data.callDataSize = _ext.data.size(); - m_data.address = eth2llvm(fromAddress(_ext.myAddress)); - m_data.caller = eth2llvm(fromAddress(_ext.caller)); - m_data.origin = eth2llvm(fromAddress(_ext.origin)); - m_data.callValue = eth2llvm(_ext.value); - m_data.coinBase = eth2llvm(fromAddress(_ext.currentBlock.coinbaseAddress)); - m_data.difficulty = eth2llvm(_ext.currentBlock.difficulty); - m_data.gasLimit = eth2llvm(_ext.currentBlock.gasLimit); + m_data.address = eth2jit(fromAddress(_ext.myAddress)); + m_data.caller = eth2jit(fromAddress(_ext.caller)); + m_data.origin = eth2jit(fromAddress(_ext.origin)); + m_data.callValue = eth2jit(_ext.value); + m_data.coinBase = eth2jit(fromAddress(_ext.currentBlock.coinbaseAddress)); + m_data.difficulty = eth2jit(_ext.currentBlock.difficulty); + m_data.gasLimit = eth2jit(_ext.currentBlock.gasLimit); m_data.number = static_cast(_ext.currentBlock.number); m_data.timestamp = static_cast(_ext.currentBlock.timestamp); m_data.code = _ext.code.data(); m_data.codeSize = _ext.code.size(); - m_data.codeHash = eth2llvm(sha3(_ext.code)); + m_data.codeHash = eth2jit(sha3(_ext.code)); m_context.init(m_data, reinterpret_cast(&_ext)); auto exitCode = evmjit::JIT::exec(m_context); switch (exitCode) { case evmjit::ReturnCode::Suicide: - _ext.suicide(right160(llvm2eth(m_data.address))); + _ext.suicide(right160(jit2eth(m_data.address))); break; case evmjit::ReturnCode::BadJumpDestination: diff --git a/libevmjit-cpp/Utils.h b/libevmjit-cpp/Utils.h index d52861fcc..ebb6ad97d 100644 --- a/libevmjit-cpp/Utils.h +++ b/libevmjit-cpp/Utils.h @@ -7,34 +7,35 @@ namespace dev namespace eth { -inline u256 llvm2eth(evmjit::i256 _i) +/// Converts EVM JIT representation of 256-bit integer to eth type dev::u256. +inline u256 jit2eth(evmjit::i256 _i) { - u256 u = 0; - u |= _i.d; + u256 u = _i.words[3]; u <<= 64; - u |= _i.c; + u |= _i.words[2]; u <<= 64; - u |= _i.b; + u |= _i.words[1]; u <<= 64; - u |= _i.a; + u |= _i.words[0]; return u; } -inline evmjit::i256 eth2llvm(u256 _u) +/// Converts eth type dev::u256 to EVM JIT representation of 256-bit integer. +inline evmjit::i256 eth2jit(u256 _u) { evmjit::i256 i; - u256 mask = 0xFFFFFFFFFFFFFFFF; - i.a = static_cast(_u & mask); + i.words[0] = static_cast(_u); _u >>= 64; - i.b = static_cast(_u & mask); + i.words[1] = static_cast(_u); _u >>= 64; - i.c = static_cast(_u & mask); + i.words[2] = static_cast(_u); _u >>= 64; - i.d = static_cast(_u & mask); + i.words[3] = static_cast(_u); return i; } -inline evmjit::h256 eth2llvm(h256 _u) +/// Converts eth type dev::h256 to EVM JIT representation of 256-bit hash value. +inline evmjit::h256 eth2jit(h256 _u) { /// Just directly copies memory return *(evmjit::h256*)&_u; From 4c42a5df4456c68dc76891c7b106199ff7565e3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 18 May 2015 14:44:47 +0200 Subject: [PATCH 37/72] Remove evmcc tool. --- CMakeLists.txt | 4 - evmcc/CMakeLists.txt | 18 ---- evmcc/evmcc.cpp | 210 ------------------------------------------- 3 files changed, 232 deletions(-) delete mode 100644 evmcc/CMakeLists.txt delete mode 100644 evmcc/evmcc.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index dc7c50017..e1db7da06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,3 @@ add_subdirectory(libevmjit) if(EVMJIT_CPP) add_subdirectory(libevmjit-cpp) endif() - -if(EVMJIT_TOOLS) - add_subdirectory(evmcc) -endif() diff --git a/evmcc/CMakeLists.txt b/evmcc/CMakeLists.txt deleted file mode 100644 index 4ffbf5fb5..000000000 --- a/evmcc/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -set(TARGET_NAME evmcc) - -set(SOURCES - evmcc.cpp -) -source_group("" FILES ${SOURCES}) - -add_executable(${TARGET_NAME} ${SOURCES}) -set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "tools") - -include_directories(../..) -include_directories(${LLVM_INCLUDE_DIRS}) -include_directories(${Boost_INCLUDE_DIRS}) - -target_link_libraries(${TARGET_NAME} ethereum) -target_link_libraries(${TARGET_NAME} ${Boost_PROGRAM_OPTIONS_LIBRARIES}) - -install(TARGETS ${TARGET_NAME} DESTINATION bin ) \ No newline at end of file diff --git a/evmcc/evmcc.cpp b/evmcc/evmcc.cpp deleted file mode 100644 index e86f948f2..000000000 --- a/evmcc/evmcc.cpp +++ /dev/null @@ -1,210 +0,0 @@ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - - -void parseProgramOptions(int _argc, char** _argv, boost::program_options::variables_map& _varMap) -{ - namespace opt = boost::program_options; - - opt::options_description explicitOpts("Allowed options"); - explicitOpts.add_options() - ("help,h", "show usage information") - ("compile,c", "compile the code to LLVM IR") - ("interpret,i", "compile the code to LLVM IR and execute") - ("gas,g", opt::value(), "set initial gas for execution") - ("disassemble,d", "dissassemble the code") - ("dump-cfg", "dump control flow graph to graphviz file") - ("dont-optimize", "turn off optimizations") - ("optimize-stack", "optimize stack use between basic blocks (default: on)") - ("rewrite-switch", "rewrite LLVM switch to branches (default: on)") - ("output-ll", opt::value(), "dump generated LLVM IR to file") - ("output-bc", opt::value(), "dump generated LLVM bitcode to file") - ("show-logs", "output LOG statements to stderr") - ("verbose,V", "enable verbose output"); - - opt::options_description implicitOpts("Input files"); - implicitOpts.add_options() - ("input-file", opt::value(), "input file"); - - opt::options_description allOpts(""); - allOpts.add(explicitOpts).add(implicitOpts); - - opt::positional_options_description inputOpts; - inputOpts.add("input-file", 1); - - const char* errorMsg = nullptr; - try - { - auto parser = opt::command_line_parser(_argc, _argv).options(allOpts).positional(inputOpts); - opt::store(parser.run(), _varMap); - opt::notify(_varMap); - } - catch (boost::program_options::error& err) - { - errorMsg = err.what(); - } - - if (!errorMsg && _varMap.count("input-file") == 0) - errorMsg = "missing input file name"; - - if (_varMap.count("disassemble") == 0 - && _varMap.count("compile") == 0 - && _varMap.count("interpret") == 0) - { - errorMsg = "at least one of -c, -i, -d is required"; - } - - if (errorMsg || _varMap.count("help")) - { - if (errorMsg) - std::cerr << "Error: " << errorMsg << std::endl; - - std::cout << "Usage: " << _argv[0] << " input-file " << std::endl - << explicitOpts << std::endl; - std::exit(errorMsg ? 1 : 0); - } -} - -int main(int argc, char** argv) -{ - llvm::sys::PrintStackTraceOnErrorSignal(); - llvm::PrettyStackTraceProgram X(argc, argv); - - boost::program_options::variables_map options; - parseProgramOptions(argc, argv, options); - - auto inputFile = options["input-file"].as(); - std::ifstream ifs(inputFile); - if (!ifs.is_open()) - { - std::cerr << "cannot open input file " << inputFile << std::endl; - exit(1); - } - - std::string src((std::istreambuf_iterator(ifs)), - (std::istreambuf_iterator())); - - boost::algorithm::trim(src); - - using namespace dev; - - bytes bytecode = fromHex(src); - - if (options.count("disassemble")) - { - std::string assembly = eth::disassemble(bytecode); - std::cout << assembly << std::endl; - } - - if (options.count("compile") || options.count("interpret")) - { - size_t initialGas = 10000; - - if (options.count("gas")) - initialGas = options["gas"].as(); - - auto compilationStartTime = std::chrono::high_resolution_clock::now(); - - eth::jit::Compiler::Options compilerOptions; - compilerOptions.dumpCFG = options.count("dump-cfg") > 0; - bool optimize = options.count("dont-optimize") == 0; - compilerOptions.optimizeStack = optimize || options.count("optimize-stack") > 0; - compilerOptions.rewriteSwitchToBranches = optimize || options.count("rewrite-switch") > 0; - - auto compiler = eth::jit::Compiler(compilerOptions); - auto module = compiler.compile(bytecode, "main"); - - auto compilationEndTime = std::chrono::high_resolution_clock::now(); - - module->dump(); - - if (options.count("output-ll")) - { - auto outputFile = options["output-ll"].as(); - std::ofstream ofs(outputFile); - if (!ofs.is_open()) - { - std::cerr << "cannot open output file " << outputFile << std::endl; - exit(1); - } - llvm::raw_os_ostream ros(ofs); - module->print(ros, nullptr); - ofs.close(); - } - - if (options.count("output-bc")) - { - auto outputFile = options["output-bc"].as(); - std::ofstream ofs(outputFile); - if (!ofs.is_open()) - { - std::cerr << "cannot open output file " << outputFile << std::endl; - exit(1); - } - llvm::raw_os_ostream ros(ofs); - llvm::WriteBitcodeToFile(module.get(), ros); - ros.flush(); - ofs.close(); - } - - if (options.count("verbose")) - { - std::cerr << "*** Compilation time: " - << std::chrono::duration_cast(compilationEndTime - compilationStartTime).count() - << std::endl; - } - - if (options.count("interpret")) - { - using namespace eth::jit; - - ExecutionEngine engine; - eth::jit::u256 gas = initialGas; - - // Create random runtime data - RuntimeData data; - data.set(RuntimeData::Gas, gas); - data.set(RuntimeData::Address, (u160)Address(1122334455667788)); - data.set(RuntimeData::Caller, (u160)Address(0xfacefacefaceface)); - data.set(RuntimeData::Origin, (u160)Address(101010101010101010)); - data.set(RuntimeData::CallValue, 0xabcd); - data.set(RuntimeData::CallDataSize, 3); - data.set(RuntimeData::GasPrice, 1003); - data.set(RuntimeData::CoinBase, (u160)Address(101010101010101015)); - data.set(RuntimeData::TimeStamp, 1005); - data.set(RuntimeData::Number, 1006); - data.set(RuntimeData::Difficulty, 16); - data.set(RuntimeData::GasLimit, 1008); - data.set(RuntimeData::CodeSize, bytecode.size()); - data.callData = (uint8_t*)"abc"; - data.code = bytecode.data(); - - // BROKEN: env_* functions must be implemented & RuntimeData struct created - // TODO: Do not compile module again - auto result = engine.run(bytecode, &data, nullptr); - return static_cast(result); - } - } - - return 0; -} From 3b943f850a1c09564206eaa2405175ceb2ed4e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 May 2015 10:19:14 +0200 Subject: [PATCH 38/72] Do not override CMAKE_CXX_FLAGS in evmjit. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a0e9c1f46..280fec212 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,7 @@ set(CMAKE_AUTOMOC OFF) if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") else() - set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas") + set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}") endif() if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") From 81ad029af9d08c1aa248c6850414331e87fac35f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 May 2015 14:02:32 +0200 Subject: [PATCH 39/72] MSVC fixes. --- include/evmjit/JIT.h | 4 ++-- libevmjit/Cache.h | 2 ++ libevmjit/JIT.cpp | 2 ++ libevmjit/RuntimeManager.h | 2 ++ 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index c12637a25..adca983d9 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -35,7 +35,7 @@ inline bool operator==(h256 _h1, h256 _h2) /// Representation of 256-bit value binary compatible with LLVM i256 struct i256 { - uint64_t words[4] = {0,}; + uint64_t words[4]; i256() = default; i256(h256 _h) { *this = *reinterpret_cast(&_h); } @@ -149,7 +149,7 @@ public: /// Returns `true` if the EVM code has been compiled and loaded into memory. /// In this case the code can be executed without overhead. /// \param _codeHash The Keccak hash of the EVM code. - static bool isCodeReady(h256 const& _codeHash); + EXPORT static bool isCodeReady(h256 const& _codeHash); EXPORT static ReturnCode exec(ExecutionContext& _context); }; diff --git a/libevmjit/Cache.h b/libevmjit/Cache.h index 2f152c9f8..5de4222fc 100644 --- a/libevmjit/Cache.h +++ b/libevmjit/Cache.h @@ -3,7 +3,9 @@ #include #include +#include "preprocessor/llvm_includes_start.h" #include +#include "preprocessor/llvm_includes_end.h" namespace llvm { diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp index f8b4d4c00..6a9b7f932 100644 --- a/libevmjit/JIT.cpp +++ b/libevmjit/JIT.cpp @@ -1,5 +1,7 @@ #include "evmjit/JIT.h" +#include + #include "preprocessor/llvm_includes_start.h" #include #include diff --git a/libevmjit/RuntimeManager.h b/libevmjit/RuntimeManager.h index 5ce749933..98d5b3132 100644 --- a/libevmjit/RuntimeManager.h +++ b/libevmjit/RuntimeManager.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "CompilerHelper.h" #include "Type.h" #include "Instruction.h" From 38ffad65305c2cc4cd07d9db5356fd32cccf3e48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 May 2015 14:03:18 +0200 Subject: [PATCH 40/72] Adjustments for llvm::IRBuilder API changes. --- libevmjit/Arith256.cpp | 4 ++-- libevmjit/Compiler.cpp | 2 +- libevmjit/GasMeter.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index 6c1f8f160..066596e4c 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -161,8 +161,8 @@ llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char builder.SetInsertPoint(mainBB); auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type); // both y and r are non-zero - auto yLz = builder.CreateCall2(ctlzIntr, yArg, builder.getInt1(true), "y.lz"); - auto rLz = builder.CreateCall2(ctlzIntr, r0, builder.getInt1(true), "r.lz"); + auto yLz = builder.CreateCall(ctlzIntr, {yArg, builder.getInt1(true)}, "y.lz"); + auto rLz = builder.CreateCall(ctlzIntr, {r0, builder.getInt1(true)}, "r.lz"); auto i0 = builder.CreateNUWSub(yLz, rLz, "i0"); auto y0 = builder.CreateShl(yArg, i0); builder.CreateBr(loopBB); diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index a7006dcde..3ec2af203 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -148,7 +148,7 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp"); m_builder.CreateStore(fp, jmpBufWords); auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave); - auto sp = m_builder.CreateCall(stacksave, "sp"); + auto sp = m_builder.CreateCall(stacksave, {}, "sp"); auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp"); m_builder.CreateStore(sp, jmpBufSp); auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp); diff --git a/libevmjit/GasMeter.cpp b/libevmjit/GasMeter.cpp index c0a2ed287..4cd053316 100644 --- a/libevmjit/GasMeter.cpp +++ b/libevmjit/GasMeter.cpp @@ -217,7 +217,7 @@ void GasMeter::countExp(llvm::Value* _exponent) // OPT: Can gas update be done in exp algorithm? auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); - auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false)); + auto lz256 = m_builder.CreateCall(ctlz, {_exponent, m_builder.getInt1(false)}); auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz"); auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits"); auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8)); From a6ef3f40d1249d390f5c26e3fe2bad74594e1145 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 19 May 2015 17:44:07 +0200 Subject: [PATCH 41/72] Avoid forbidden function pointer cast. --- include/evmjit/JIT.h | 4 ++-- libevmjit/ExecutionEngine.cpp | 2 +- libevmjit/JIT.cpp | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index 446dd9e56..c9ddde705 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -28,8 +28,8 @@ public: private: friend class dev::eth::jit::ExecutionEngine; - static void* getCode(h256 _codeHash); - static void mapCode(h256 _codeHash, void* _funcAddr); + static uint64_t getCode(h256 _codeHash); + static void mapCode(h256 _codeHash, uint64_t _funcAddr); }; } diff --git a/libevmjit/ExecutionEngine.cpp b/libevmjit/ExecutionEngine.cpp index 08ca403b5..e5abb36b3 100644 --- a/libevmjit/ExecutionEngine.cpp +++ b/libevmjit/ExecutionEngine.cpp @@ -183,7 +183,7 @@ ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); if (!CHECK(entryFuncPtr)) return ReturnCode::LLVMLinkError; - JIT::mapCode(_data->codeHash, (void*)entryFuncPtr); // FIXME: Remove cast + JIT::mapCode(_data->codeHash, (uint64_t)entryFuncPtr); // FIXME: Remove cast } listener->stateChanged(ExecState::Execution); diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp index 9774c7396..8617fab6d 100644 --- a/libevmjit/JIT.cpp +++ b/libevmjit/JIT.cpp @@ -12,7 +12,7 @@ namespace class JITImpl: JIT { public: - std::unordered_map codeMap; + std::unordered_map codeMap; static JITImpl& instance() { @@ -28,16 +28,16 @@ bool JIT::isCodeReady(h256 _codeHash) return JITImpl::instance().codeMap.count(_codeHash) != 0; } -void* JIT::getCode(h256 _codeHash) +uint64_t JIT::getCode(h256 _codeHash) { auto& codeMap = JITImpl::instance().codeMap; auto it = codeMap.find(_codeHash); if (it != codeMap.end()) return it->second; - return nullptr; + return 0; } -void JIT::mapCode(h256 _codeHash, void* _funcAddr) +void JIT::mapCode(h256 _codeHash, uint64_t _funcAddr) { JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr)); } From c8c2e18dc75b9f100d013d9511a2750ea920e3a0 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Tue, 19 May 2015 19:51:38 +0200 Subject: [PATCH 42/72] Move non-cryptopp dependent stuff into devcore. --- libevmjit-cpp/Env.cpp | 2 +- libevmjit-cpp/JitVM.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index a5a60f48c..d1f239f9f 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -1,6 +1,6 @@ #pragma GCC diagnostic ignored "-Wconversion" -#include +#include #include #include #include diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 7acbec5c1..6efd4c31a 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -4,7 +4,7 @@ #include "JitVM.h" #include -#include +#include #include #include #include From 5d4fbd4c44aa373082263c2419ebc3065eea39b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 21 May 2015 13:11:13 +0200 Subject: [PATCH 43/72] Apply recent VM interface changes to EVM JIT. --- libevmjit-cpp/Env.cpp | 25 +++++++++++++++---------- libevmjit-cpp/JitVM.h | 6 +++++- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index d1f239f9f..86a65dbab 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -64,19 +64,24 @@ extern "C" EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) { - auto value = llvm2eth(*_value); - auto receiveAddress = right160(*_receiveAddress); - auto codeAddress = right160(*_codeAddress); - const auto isCall = receiveAddress == codeAddress; // OPT: The same address pointer can be used if not CODECALL + CallParameters params; + params.value = llvm2eth(*_value); + params.senderAddress = _env->myAddress; + params.receiveAddress = right160(*_receiveAddress); + params.codeAddress = right160(*_codeAddress); + params.data = {_inBeg, _inSize}; + params.out = {_outBeg, _outSize}; + params.onOp = {}; + const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL *io_gas -= _callGas; if (*io_gas < 0) return false; - if (isCall && !_env->exists(receiveAddress)) + if (isCall && !_env->exists(params.receiveAddress)) *io_gas -= static_cast(c_callNewAccountGas); // no underflow, *io_gas non-negative before - if (value > 0) // value transfer + if (params.value > 0) // value transfer { /*static*/ assert(c_callValueTransferGas > c_callStipend && "Overflow possible"); *io_gas -= static_cast(c_callValueTransferGas); // no underflow @@ -87,11 +92,11 @@ extern "C" return false; auto ret = false; - auto callGas = u256{_callGas}; - if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) - ret = _env->call(receiveAddress, value, {_inBeg, _inSize}, callGas, {_outBeg, _outSize}, {}, {}, codeAddress); + params.gas = u256{_callGas}; + if (_env->balance(_env->myAddress) >= params.value && _env->depth < 1024) + ret = _env->call(params); - *io_gas += static_cast(callGas); // it is never more than initial _callGas + *io_gas += static_cast(params.gas); // it is never more than initial _callGas return ret; } diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 58caa3648..38ef9ff61 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -12,10 +12,14 @@ class JitVM: public VMFace { virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual u256 gas() const noexcept { return m_gas; } + virtual void reset(u256 const& _gas = 0) noexcept { m_gas = _gas; } + private: friend class VMFactory; - explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} + explicit JitVM(u256 _gas = 0): m_gas(_gas) {} + u256 m_gas; jit::RuntimeData m_data; jit::ExecutionEngine m_engine; std::unique_ptr m_fallbackVM; ///< VM used in case of input data rejected by JIT From df78960414fd45f8f1dfa912de3384f9bf39be27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 21 May 2015 14:28:19 +0200 Subject: [PATCH 44/72] Remove LLVM cmake files workaround. --- CMakeLists.txt | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e1db7da06..c57188bbb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,22 +16,11 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") endif() # LLVM -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}") - add_definitions(${LLVM_DEFINITIONS}) - # TODO: bitwriter is needed only for evmcc - llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen bitwriter ipo) -else() - # Workaround for Ubuntu broken LLVM package - message(STATUS "Using llvm-3.7-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake") - execute_process(COMMAND llvm-config-3.7 --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 -lLLVMExecutionEngine -lLLVMRuntimeDyld -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMMC -lLLVMMCDisassembler -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.7/lib) -endif() +find_package(LLVM REQUIRED CONFIG) +message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") +add_definitions(${LLVM_DEFINITIONS}) +llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen ipo) get_filename_component(EVMJIT_INCLUDE_DIR include ABSOLUTE) From 3202abb91e7212003933ed18489ede6ca2d38b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 21 May 2015 17:54:37 +0200 Subject: [PATCH 45/72] Cleanup unused code. --- include/evmjit/JIT.h | 2 +- libevmjit/Array.cpp | 2 -- libevmjit/Ext.h | 1 - 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index adca983d9..85c74ba18 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -129,7 +129,7 @@ public: bytes_ref getReturnData() const; -private: +protected: RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract. Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract. byte* m_memData = nullptr; diff --git a/libevmjit/Array.cpp b/libevmjit/Array.cpp index a76776c69..e77ceefe4 100644 --- a/libevmjit/Array.cpp +++ b/libevmjit/Array.cpp @@ -18,7 +18,6 @@ namespace jit { static const auto c_reallocStep = 1; -static const auto c_reallocMultipier = 2; llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list const& _args, llvm::Twine const& _name) { @@ -57,7 +56,6 @@ llvm::Function* Array::createArrayPushFunc() m_builder.SetInsertPoint(reallocBB); auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap"); - //newCap = m_builder.CreateNUWMul(newCap, m_builder.getInt64(c_reallocMultipier)); auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32 auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes"); auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes"); diff --git a/libevmjit/Ext.h b/libevmjit/Ext.h index 760f77df5..2af074f4a 100644 --- a/libevmjit/Ext.h +++ b/libevmjit/Ext.h @@ -62,7 +62,6 @@ private: Memory& m_memoryMan; llvm::Value* m_size; - llvm::Value* m_data = nullptr; std::array::value> m_funcs; std::array m_argAllocas; From 04a123e9a1c27adad19a00c949c19bea3df88dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 27 May 2015 13:23:00 +0200 Subject: [PATCH 46/72] Change VM interface to return a copy of output. --- libevmjit-cpp/JitVM.cpp | 4 ++-- libevmjit-cpp/JitVM.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 5193168a4..ac8df545f 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -18,7 +18,7 @@ namespace eth extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below -bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) +bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) { using namespace jit; @@ -33,7 +33,7 @@ bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, ui { cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter"; m_fallbackVM = VMFactory::create(VMKind::Interpreter); - return m_fallbackVM->go(io_gas, _ext, _onOp, _step); + return m_fallbackVM->execImpl(io_gas, _ext, _onOp, _step); } m_data.gas = static_cast(io_gas); diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index e6864f885..7c8b4ceb8 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -11,7 +11,7 @@ namespace eth class JitVM: public VMFace { public: - virtual bytesConstRef go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; private: jit::RuntimeData m_data; From 565744c9e3a2e91119c7ebb3d6663e0569ed94e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 May 2015 08:56:21 +0200 Subject: [PATCH 47/72] Change the way execution results are collected. Changes handling ExecutionResult by Executive. From now execution results are collected on if a storage for results (ExecutionResult) is provided to an Executiove instance up front. This change allow better output management for calls - VM interface improved. --- libevmjit-cpp/JitVM.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 7c8b4ceb8..03c3c8880 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -11,7 +11,7 @@ namespace eth class JitVM: public VMFace { public: - virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; + virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) override final; private: jit::RuntimeData m_data; From 17d7d715543719cd1c1dc5262b1af2ebc82a7e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 28 May 2015 10:29:16 +0200 Subject: [PATCH 48/72] Kill steps limit option in VM. --- libevmjit-cpp/JitVM.cpp | 4 ++-- libevmjit-cpp/JitVM.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index ac8df545f..0d6a6e00a 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -18,7 +18,7 @@ namespace eth extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below -bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) +bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) { using namespace jit; @@ -33,7 +33,7 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on { cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter"; m_fallbackVM = VMFactory::create(VMKind::Interpreter); - return m_fallbackVM->execImpl(io_gas, _ext, _onOp, _step); + return m_fallbackVM->execImpl(io_gas, _ext, _onOp); } m_data.gas = static_cast(io_gas); diff --git a/libevmjit-cpp/JitVM.h b/libevmjit-cpp/JitVM.h index 03c3c8880..e97abd83b 100644 --- a/libevmjit-cpp/JitVM.h +++ b/libevmjit-cpp/JitVM.h @@ -11,7 +11,7 @@ namespace eth class JitVM: public VMFace { public: - virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _steps) override final; + virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final; private: jit::RuntimeData m_data; From 0871668ebf570f9f290a4714c6f17f63c4d0d67d Mon Sep 17 00:00:00 2001 From: winsvega Date: Fri, 22 May 2015 16:21:34 +0300 Subject: [PATCH 49/72] evmJit warnings fix --- libevmjit-cpp/Env.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/libevmjit-cpp/Env.cpp b/libevmjit-cpp/Env.cpp index 86a65dbab..4d7865bd0 100644 --- a/libevmjit-cpp/Env.cpp +++ b/libevmjit-cpp/Env.cpp @@ -54,7 +54,7 @@ extern "C" if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024) { u256 gas = *io_gas; - h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight); + h256 address(_env->create(endowment, gas, {_initBeg, (size_t)_initSize}, {}), h256::AlignRight); *io_gas = static_cast(gas); *o_address = address; } @@ -69,8 +69,8 @@ extern "C" params.senderAddress = _env->myAddress; params.receiveAddress = right160(*_receiveAddress); params.codeAddress = right160(*_codeAddress); - params.data = {_inBeg, _inSize}; - params.out = {_outBeg, _outSize}; + params.data = {_inBeg, (size_t)_inSize}; + params.out = {_outBeg, (size_t)_outSize}; params.onOp = {}; const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL @@ -102,7 +102,7 @@ extern "C" EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash) { - auto hash = sha3({_begin, _size}); + auto hash = sha3({_begin, (size_t)_size}); *o_hash = hash; } @@ -130,7 +130,7 @@ extern "C" if (_topic4) topics.push_back(*_topic4); - _env->log(std::move(topics), {_beg, _size}); + _env->log(std::move(topics), {_beg, (size_t)_size}); } } From 8aa722658d7c3325eb24fd79073f3b8d44e6d3a1 Mon Sep 17 00:00:00 2001 From: arkpar Date: Mon, 8 Jun 2015 10:26:07 +0200 Subject: [PATCH 50/72] codeHash in ExtVM --- libevmjit-cpp/JitVM.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 5193168a4..6dafa3f39 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -51,7 +51,7 @@ bytesConstRef JitVM::go(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp, ui m_data.timestamp = static_cast(_ext.currentBlock.timestamp); m_data.code = _ext.code.data(); m_data.codeSize = _ext.code.size(); - m_data.codeHash = eth2llvm(sha3(_ext.code)); + m_data.codeHash = eth2llvm(_ext.codeHash); auto env = reinterpret_cast(&_ext); auto exitCode = m_engine.run(&m_data, env); From 9359223e694f28c1aa30717694a243d8fb057f07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 10 Jun 2015 13:04:19 +0200 Subject: [PATCH 51/72] Remove memory leak detector. Is it not thread-safe. --- libevmjit/Array.cpp | 43 ++----------------------------------------- 1 file changed, 2 insertions(+), 41 deletions(-) diff --git a/libevmjit/Array.cpp b/libevmjit/Array.cpp index 3266038db..0b511a058 100644 --- a/libevmjit/Array.cpp +++ b/libevmjit/Array.cpp @@ -9,8 +9,6 @@ #include "Runtime.h" #include "Utils.h" -#include // DEBUG only - namespace dev { namespace eth @@ -269,52 +267,15 @@ void Array::extend(llvm::Value* _arrayPtr, llvm::Value* _size) } } -namespace -{ - struct AllocatedMemoryWatchdog - { - std::set allocatedMemory; - - ~AllocatedMemoryWatchdog() - { - if (!allocatedMemory.empty()) - { - DLOG(mem) << allocatedMemory.size() << " MEM LEAKS!\n"; - for (auto&& leak : allocatedMemory) - DLOG(mem) << "\t" << leak << "\n"; - } - } - }; - - AllocatedMemoryWatchdog watchdog; -} - extern "C" { - using namespace dev::eth::jit; - EXPORT void* ext_realloc(void* _data, size_t _size) noexcept { - //std::cerr << "REALLOC: " << _data << " [" << _size << "]" << std::endl; - auto newData = std::realloc(_data, _size); - if (_data != newData) - { - DLOG(mem) << "REALLOC: " << newData << " <- " << _data << " [" << _size << "]\n"; - watchdog.allocatedMemory.erase(_data); - watchdog.allocatedMemory.insert(newData); - } - return newData; + return std::realloc(_data, _size); } EXPORT void ext_free(void* _data) noexcept { std::free(_data); - if (_data) - { - DLOG(mem) << "FREE : " << _data << "\n"; - watchdog.allocatedMemory.erase(_data); - } } - -} // extern "C" - +} From 787df33341d084c2d69e1e2dabe9a009744f8dcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 10 Jun 2015 14:22:44 +0200 Subject: [PATCH 52/72] Protect EVM JIT cache with mutex. Fixes ethereum/cpp-ethereum#2086. --- libevmjit/Cache.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/libevmjit/Cache.cpp b/libevmjit/Cache.cpp index 47a6386e9..42ccf44ac 100644 --- a/libevmjit/Cache.cpp +++ b/libevmjit/Cache.cpp @@ -1,5 +1,7 @@ #include "Cache.h" +#include + #include "preprocessor/llvm_includes_start.h" #include #include @@ -23,6 +25,8 @@ namespace jit namespace { + using Guard = std::lock_guard; + std::mutex x_cacheMutex; CacheMode g_mode; llvm::MemoryBuffer* g_lastObject; ExecutionEngineListener* g_listener; @@ -43,6 +47,9 @@ namespace ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener) { static ObjectCache objectCache; + + Guard g{x_cacheMutex}; + g_mode = _mode; g_listener = _listener; return &objectCache; @@ -50,6 +57,8 @@ ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _li void Cache::clear() { + Guard g{x_cacheMutex}; + using namespace llvm::sys; llvm::SmallString<256> cachePath; path::system_temp_directory(false, cachePath); @@ -62,6 +71,8 @@ void Cache::clear() void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map& _funcCache) { + Guard g{x_cacheMutex}; + // TODO: Cache dir should be in one place using namespace llvm::sys; llvm::SmallString<256> cachePath; @@ -92,11 +103,14 @@ void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map Cache::getObject(std::string const& id) { + Guard g{x_cacheMutex}; + if (g_mode != CacheMode::on && g_mode != CacheMode::read) return nullptr; - if (g_listener) - g_listener->stateChanged(ExecState::CacheLoad); + // TODO: Disabled because is not thread-safe. + //if (g_listener) + // g_listener->stateChanged(ExecState::CacheLoad); DLOG(cache) << id << ": search\n"; if (!CHECK(!g_lastObject)) @@ -136,12 +150,15 @@ std::unique_ptr Cache::getObject(std::string const& id) void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) { + Guard g{x_cacheMutex}; + // Only in "on" and "write" mode if (g_mode != CacheMode::on && g_mode != CacheMode::write) return; - if (g_listener) - g_listener->stateChanged(ExecState::CacheWrite); + // TODO: Disabled because is not thread-safe. + // if (g_listener) + // g_listener->stateChanged(ExecState::CacheWrite); auto&& id = _module->getModuleIdentifier(); llvm::SmallString<256> cachePath; @@ -161,6 +178,8 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module) { + Guard g{x_cacheMutex}; + DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; auto o = g_lastObject; g_lastObject = nullptr; From e5eda3bb5f8678cd295c28f216f6e590cedf4b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 10 Jun 2015 18:01:37 +0200 Subject: [PATCH 53/72] Disable some warnings in LLVM headers for GCC/clang compilers. --- libevmjit/preprocessor/llvm_includes_end.h | 4 ++++ libevmjit/preprocessor/llvm_includes_start.h | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/libevmjit/preprocessor/llvm_includes_end.h b/libevmjit/preprocessor/llvm_includes_end.h index 643e03064..2ead6dda3 100644 --- a/libevmjit/preprocessor/llvm_includes_end.h +++ b/libevmjit/preprocessor/llvm_includes_end.h @@ -1,3 +1,7 @@ #if defined(_MSC_VER) #pragma warning(pop) +#elif defined(__clang__) + #pragma clang diagnostic pop +#else + #pragma GCC diagnostic pop #endif diff --git a/libevmjit/preprocessor/llvm_includes_start.h b/libevmjit/preprocessor/llvm_includes_start.h index 9499f9835..9077bf43f 100644 --- a/libevmjit/preprocessor/llvm_includes_start.h +++ b/libevmjit/preprocessor/llvm_includes_start.h @@ -1,4 +1,12 @@ #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4267 4244 4800) +#elif defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunused-parameter" + #pragma clang diagnostic ignored "-Wconversion" +#else + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-parameter" + #pragma GCC diagnostic ignored "-Wconversion" #endif From fe03309244531970acd27ecc204440911bdf557f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 11 Jun 2015 10:59:14 +0200 Subject: [PATCH 54/72] Skip all STOPs in the end in EVM JIT compiler --- libevmjit/Compiler.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 3ec2af203..be980f4be 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -49,6 +49,13 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn return _curr + offset; }; + // Skip all STOPs in the end + for (; _codeEnd != _codeBegin; --_codeEnd) + { + if (*(_codeEnd - 1) != static_cast(Instruction::STOP)) + break; + } + auto begin = _codeBegin; // begin of current block bool nextJumpDest = false; for (auto curr = begin, next = begin; curr != _codeEnd; curr = next) @@ -81,6 +88,7 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn if (isEnd) { auto beginIdx = begin - _codeBegin; + std::cerr << "BB #" << beginIdx << ": " << (next - begin) << "B\n"; m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, m_builder, nextJumpDest)); nextJumpDest = false; From afe4a477a5d32c088a8cff6ef765d2f3e8b30d93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 11 Jun 2015 16:10:41 +0200 Subject: [PATCH 55/72] Remove debug message. --- libevmjit/Compiler.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index be980f4be..55a8b9203 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -88,7 +88,6 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn if (isEnd) { auto beginIdx = begin - _codeBegin; - std::cerr << "BB #" << beginIdx << ": " << (next - begin) << "B\n"; m_basicBlocks.emplace(std::piecewise_construct, std::forward_as_tuple(beginIdx), std::forward_as_tuple(beginIdx, begin, next, m_mainFunc, m_builder, nextJumpDest)); nextJumpDest = false; From 3e7163a1ac37997048e9ae477e76d88eb46ccacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 11 Jun 2015 16:11:27 +0200 Subject: [PATCH 56/72] Release aquired arg allocas in Ext::calldataload. --- libevmjit/Ext.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libevmjit/Ext.cpp b/libevmjit/Ext.cpp index 9b1e0983b..b8b124ff5 100644 --- a/libevmjit/Ext.cpp +++ b/libevmjit/Ext.cpp @@ -119,6 +119,7 @@ llvm::Value* Ext::calldataload(llvm::Value* _idx) auto pad = m_builder.CreateGEP(Type::Byte, result, copySize); m_builder.CreateMemSet(pad, m_builder.getInt8(0), padSize, 1); + m_argCounter = 0; // Release args allocas. TODO: This is a bad design return Endianness::toNative(m_builder, m_builder.CreateLoad(ret)); } From a713f079bfd479048cced315312ede5e2a3d9299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 11 Jun 2015 17:48:53 +0200 Subject: [PATCH 57/72] Fix unaligned copy. --- include/evmjit/JIT.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index 85c74ba18..0a938fae9 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #ifdef _MSC_VER @@ -38,7 +39,7 @@ struct i256 uint64_t words[4]; i256() = default; - i256(h256 _h) { *this = *reinterpret_cast(&_h); } + i256(h256 _h) { std::memcpy(this, &_h, sizeof(*this)); } }; // TODO: Merge with ExecutionContext From 50a827354ef3ad60c57059406bb41f30e63f120c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 10 Jun 2015 18:01:37 +0200 Subject: [PATCH 58/72] Disable some warnings in LLVM headers for GCC/clang compilers. --- libevmjit/preprocessor/llvm_includes_end.h | 4 ++++ libevmjit/preprocessor/llvm_includes_start.h | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/libevmjit/preprocessor/llvm_includes_end.h b/libevmjit/preprocessor/llvm_includes_end.h index 643e03064..2ead6dda3 100644 --- a/libevmjit/preprocessor/llvm_includes_end.h +++ b/libevmjit/preprocessor/llvm_includes_end.h @@ -1,3 +1,7 @@ #if defined(_MSC_VER) #pragma warning(pop) +#elif defined(__clang__) + #pragma clang diagnostic pop +#else + #pragma GCC diagnostic pop #endif diff --git a/libevmjit/preprocessor/llvm_includes_start.h b/libevmjit/preprocessor/llvm_includes_start.h index 9499f9835..9077bf43f 100644 --- a/libevmjit/preprocessor/llvm_includes_start.h +++ b/libevmjit/preprocessor/llvm_includes_start.h @@ -1,4 +1,12 @@ #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4267 4244 4800) +#elif defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wunused-parameter" + #pragma clang diagnostic ignored "-Wconversion" +#else + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wunused-parameter" + #pragma GCC diagnostic ignored "-Wconversion" #endif From 7230d2ee53a7b4d26f05bd3de24a54ee62481c74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 21 May 2015 14:28:19 +0200 Subject: [PATCH 59/72] Remove LLVM cmake files workaround. --- CMakeLists.txt | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4343ddc31..fa9b08f48 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,22 +16,11 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") endif() # LLVM -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}") - add_definitions(${LLVM_DEFINITIONS}) - # TODO: bitwriter is needed only for evmcc - llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen bitwriter ipo) -else() - # Workaround for Ubuntu broken LLVM package - message(STATUS "Using llvm-3.7-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake") - execute_process(COMMAND llvm-config-3.7 --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 -lLLVMExecutionEngine -lLLVMRuntimeDyld -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMMC -lLLVMMCDisassembler -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.7/lib) -endif() +find_package(LLVM REQUIRED CONFIG) +message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") +add_definitions(${LLVM_DEFINITIONS}) +llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen ipo) get_filename_component(EVMJIT_INCLUDE_DIR include ABSOLUTE) From 65406afd162bc49fcdbb714a61970f46f5168e48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 15 Jun 2015 14:43:15 +0200 Subject: [PATCH 60/72] Suppress LLVM compile warnings. --- libevmjit/Cache.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libevmjit/Cache.h b/libevmjit/Cache.h index 6b8457f4b..1d5927705 100644 --- a/libevmjit/Cache.h +++ b/libevmjit/Cache.h @@ -3,7 +3,9 @@ #include #include +#include "preprocessor/llvm_includes_start.h" #include +#include "preprocessor/llvm_includes_end.h" namespace llvm { From 3776d189a2165ae7fc27feddaea9bbc1ee545b73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 15 Jun 2015 14:43:42 +0200 Subject: [PATCH 61/72] Update llvm::IRBuilder::CreateCall to new API version. Buildbot bump. --- libevmjit/Arith256.cpp | 4 ++-- libevmjit/Compiler.cpp | 2 +- libevmjit/GasMeter.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index 029eb9370..95ca2aee5 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -167,8 +167,8 @@ llvm::Function* Arith256::getDivFunc(llvm::Type* _type) m_builder.SetInsertPoint(mainBB); auto ctlzIntr = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, _type); // both y and r are non-zero - auto yLz = m_builder.CreateCall2(ctlzIntr, yArg, m_builder.getInt1(true), "y.lz"); - auto rLz = m_builder.CreateCall2(ctlzIntr, r0, m_builder.getInt1(true), "r.lz"); + auto yLz = m_builder.CreateCall(ctlzIntr, {yArg, m_builder.getInt1(true)}, "y.lz"); + auto rLz = m_builder.CreateCall(ctlzIntr, {r0, m_builder.getInt1(true)}, "r.lz"); auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0"); auto y0 = m_builder.CreateShl(yArg, i0); m_builder.CreateBr(loopBB); diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index d5cb493ab..e49979294 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -148,7 +148,7 @@ std::unique_ptr Compiler::compile(code_iterator _begin, code_itera auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp"); m_builder.CreateStore(fp, jmpBufWords); auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave); - auto sp = m_builder.CreateCall(stacksave, "sp"); + auto sp = m_builder.CreateCall(stacksave, {}, "sp"); auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp"); m_builder.CreateStore(sp, jmpBufSp); auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp); diff --git a/libevmjit/GasMeter.cpp b/libevmjit/GasMeter.cpp index c0a2ed287..4cd053316 100644 --- a/libevmjit/GasMeter.cpp +++ b/libevmjit/GasMeter.cpp @@ -217,7 +217,7 @@ void GasMeter::countExp(llvm::Value* _exponent) // OPT: Can gas update be done in exp algorithm? auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word); - auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false)); + auto lz256 = m_builder.CreateCall(ctlz, {_exponent, m_builder.getInt1(false)}); auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz"); auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits"); auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8)); From 7c8892e47720ec94e631ed8514aa5797ca8caf60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 16 Jun 2015 09:18:53 +0200 Subject: [PATCH 62/72] Set required LLVM version to 3.7. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fa9b08f48..5da512c99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") endif() # LLVM -find_package(LLVM REQUIRED CONFIG) +find_package(LLVM 3.7 REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") add_definitions(${LLVM_DEFINITIONS}) From def5b507b898122c624e3d594859bf5da66fd414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 22 Jun 2015 11:01:13 +0200 Subject: [PATCH 63/72] Add support for clang sanitizer to cmake scripts. To build with the sanitizer support use clang compiler and set CMAKE_BUILD_TYPE to "DebugSan". More info: http://clang.llvm.org/docs/AddressSanitizer.html. --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 280fec212..d9a01255c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,7 +10,7 @@ else() set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}") endif() -if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "DebugSan") # Do not allow unresovled symbols in shared library (default on linux) set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") endif() From 714e122718c5ac69db53c1d319ac37d07fb722d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 24 Jun 2015 17:35:20 +0200 Subject: [PATCH 64/72] Cleanup issues reported by clang. --- libevmjit/Optimizer.cpp | 3 +-- libevmjit/Stack.h | 5 ----- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index d5bf7f05c..b097a2e3f 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -36,8 +36,6 @@ class LowerEVMPass : public llvm::BasicBlockPass { static char ID; - bool m_mulFuncNeeded = false; - public: LowerEVMPass(): llvm::BasicBlockPass(ID) @@ -45,6 +43,7 @@ public: virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override; + using llvm::BasicBlockPass::doFinalization; virtual bool doFinalization(llvm::Module& _module) override; }; diff --git a/libevmjit/Stack.h b/libevmjit/Stack.h index 6f7ad1c0b..ad10dae12 100644 --- a/libevmjit/Stack.h +++ b/libevmjit/Stack.h @@ -27,12 +27,7 @@ private: llvm::Function* getGetFunc(); RuntimeManager& m_runtimeManager; - - llvm::Function* m_pop = nullptr; - llvm::Function* m_push = nullptr; llvm::Function* m_get = nullptr; - llvm::Function* m_set = nullptr; - Array m_stack; }; From 6963d9f6df207cea6c0e56de1a50f89d661d064d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 24 Jun 2015 17:43:47 +0200 Subject: [PATCH 65/72] Do not compute code hash twice in VM test engine. --- libevmjit/Type.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libevmjit/Type.h b/libevmjit/Type.h index b54d101cb..5e5b9dde5 100644 --- a/libevmjit/Type.h +++ b/libevmjit/Type.h @@ -4,7 +4,7 @@ #include #include #include -#include "preprocessor/llvm_includes_end.h" // FIXME: LLVM 3.7: check if needed +#include "preprocessor/llvm_includes_end.h" #include "evmjit/JIT.h" // ReturnCode From 1d4ea994537803b9580feab3fe4c9c12b080a134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Fri, 26 Jun 2015 14:38:20 +0200 Subject: [PATCH 66/72] Fix for incorrect BYTE instruction results. It is actually a LLVM bug workaround. --- libevmjit/Compiler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 55a8b9203..00fe7f7d0 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -481,7 +481,9 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid"); auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); - auto byte = m_builder.CreateExtractElement(bytes, idx, "byte"); + // Workaround for LLVM bug. Using index of type i256 can produce incorrect results + auto safeIdx = m_builder.CreateZExt(m_builder.CreateTrunc(idx, m_builder.getIntNTy(5)), Type::Size); + auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte"); value = m_builder.CreateZExt(byte, Type::Word); value = m_builder.CreateSelect(idxValid, value, Constant::get(0)); stack.push(value); From 90a03ec025f8d0632abfd16d01413a8ad4d3cf27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Mon, 29 Jun 2015 16:33:48 +0200 Subject: [PATCH 67/72] Better BYTE fix. --- libevmjit/Compiler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index 00fe7f7d0..cde86c747 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -481,8 +481,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid"); auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); - // Workaround for LLVM bug. Using index of type i256 can produce incorrect results - auto safeIdx = m_builder.CreateZExt(m_builder.CreateTrunc(idx, m_builder.getIntNTy(5)), Type::Size); + // Workaround for LLVM bug. Using big value of index causes invalid memory access. + auto safeIdx = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5)); auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte"); value = m_builder.CreateZExt(byte, Type::Word); value = m_builder.CreateSelect(idxValid, value, Constant::get(0)); From f7c87331b49802d0e5ea0133b7fc4372ec2e7ab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Tue, 30 Jun 2015 12:18:16 +0200 Subject: [PATCH 68/72] Another LLVM bug workaround. --- libevmjit/Compiler.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index cde86c747..e51df0245 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -481,8 +481,10 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid"); auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes"); - // Workaround for LLVM bug. Using big value of index causes invalid memory access. + // TODO: Workaround for LLVM bug. Using big value of index causes invalid memory access. auto safeIdx = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5)); + // TODO: Workaround for LLVM bug. DAG Builder used sext on index instead of zext + safeIdx = m_builder.CreateZExt(safeIdx, Type::Size); auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte"); value = m_builder.CreateZExt(byte, Type::Word); value = m_builder.CreateSelect(idxValid, value, Constant::get(0)); From e4456e34faeedfc390747607cbd13a2925ac0ff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 1 Jul 2015 16:04:55 +0200 Subject: [PATCH 69/72] Cleanups. --- include/evmjit/JIT.h | 8 +++++--- libevmjit-cpp/JitVM.cpp | 2 ++ libevmjit/Arith256.cpp | 4 ++-- libevmjit/Optimizer.cpp | 2 +- libevmjit/RuntimeManager.h | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index 0a938fae9..fcb0db4e7 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -20,12 +20,13 @@ namespace evmjit using byte = uint8_t; using bytes_ref = std::tuple; +/// Representation of 256-bit hash value struct h256 { uint64_t words[4]; }; -inline bool operator==(h256 _h1, h256 _h2) +inline bool operator==(h256 const& _h1, h256 const& _h2) { return _h1.words[0] == _h2.words[0] && _h1.words[1] == _h2.words[1] && @@ -39,7 +40,7 @@ struct i256 uint64_t words[4]; i256() = default; - i256(h256 _h) { std::memcpy(this, &_h, sizeof(*this)); } + i256(h256 const& _h) { std::memcpy(this, &_h, sizeof(*this)); } }; // TODO: Merge with ExecutionContext @@ -68,6 +69,8 @@ struct RuntimeData ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN) }; + static size_t const numElements = CodeSize + 1; + int64_t gas = 0; int64_t gasPrice = 0; byte const* callData = nullptr; @@ -170,4 +173,3 @@ template<> struct hash }; }; } - diff --git a/libevmjit-cpp/JitVM.cpp b/libevmjit-cpp/JitVM.cpp index 169ce68ae..2fb2a0e67 100644 --- a/libevmjit-cpp/JitVM.cpp +++ b/libevmjit-cpp/JitVM.cpp @@ -50,6 +50,8 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on m_data.codeSize = _ext.code.size(); m_data.codeHash = eth2jit(_ext.codeHash); + // Pass pointer to ExtVMFace casted to evmjit::Env* opaque type. + // JIT will do nothing with the pointer, just pass it to Env callback functions implemented in Env.cpp. m_context.init(m_data, reinterpret_cast(&_ext)); auto exitCode = evmjit::JIT::exec(m_context); switch (exitCode) diff --git a/libevmjit/Arith256.cpp b/libevmjit/Arith256.cpp index 066596e4c..76c53c78d 100644 --- a/libevmjit/Arith256.cpp +++ b/libevmjit/Arith256.cpp @@ -132,7 +132,7 @@ namespace llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) { // Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research - // The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder + // The following algorithm also handles divisor of value 0 returning 0 for both quotient and remainder auto retType = llvm::VectorType::get(_type, 2); auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); @@ -327,7 +327,7 @@ llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module) auto qAbs = builder.CreateExtractElement(res, uint64_t(0)); auto rAbs = builder.CreateExtractElement(res, 1); - // the reminder has the same sign as dividend + // the remainder has the same sign as dividend auto rNeg = builder.CreateSub(Constant::get(0), rAbs); auto r = builder.CreateSelect(xIsNeg, rNeg, rAbs); diff --git a/libevmjit/Optimizer.cpp b/libevmjit/Optimizer.cpp index b097a2e3f..dea3ea2a0 100644 --- a/libevmjit/Optimizer.cpp +++ b/libevmjit/Optimizer.cpp @@ -32,7 +32,7 @@ bool optimize(llvm::Module& _module) namespace { -class LowerEVMPass : public llvm::BasicBlockPass +class LowerEVMPass: public llvm::BasicBlockPass { static char ID; diff --git a/libevmjit/RuntimeManager.h b/libevmjit/RuntimeManager.h index 98d5b3132..8c0728aaf 100644 --- a/libevmjit/RuntimeManager.h +++ b/libevmjit/RuntimeManager.h @@ -63,7 +63,7 @@ private: llvm::Value* m_memPtr = nullptr; llvm::Value* m_envPtr = nullptr; - std::array(RuntimeData::Index::CodeSize) + 1> m_dataElts; + std::array m_dataElts; llvm::Value* m_stackSize = nullptr; llvm::Function* m_checkStackLimit = nullptr; From 957f4558c47f2288e9795b0b7a8a37b75efc2031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 1 Jul 2015 16:14:53 +0200 Subject: [PATCH 70/72] Cleanups. --- libevmjit/Compiler.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libevmjit/Compiler.cpp b/libevmjit/Compiler.cpp index e51df0245..a44e4e890 100644 --- a/libevmjit/Compiler.cpp +++ b/libevmjit/Compiler.cpp @@ -51,10 +51,8 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn // Skip all STOPs in the end for (; _codeEnd != _codeBegin; --_codeEnd) - { if (*(_codeEnd - 1) != static_cast(Instruction::STOP)) break; - } auto begin = _codeBegin; // begin of current block bool nextJumpDest = false; @@ -977,4 +975,3 @@ void Compiler::dump() } } } - From 51bacd2525d1433f037e3b054247ad7873c4c29d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Wed, 1 Jul 2015 18:15:18 +0200 Subject: [PATCH 71/72] Separate compile and execute jobs in JIT. --- include/evmjit/JIT.h | 4 +-- libevmjit/JIT.cpp | 82 ++++++++++++++++++++++---------------------- 2 files changed, 42 insertions(+), 44 deletions(-) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index fcb0db4e7..8d6f9bbd0 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -107,9 +107,7 @@ enum class ReturnCode Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected // Internal error codes - LLVMConfigError = -101, - LLVMCompileError = -102, - LLVMLinkError = -103, + LLVMError = -101, UnexpectedException = -111, diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp index 6a9b7f932..89d2e63eb 100644 --- a/libevmjit/JIT.cpp +++ b/libevmjit/JIT.cpp @@ -97,6 +97,8 @@ public: ExecFunc getExecFunc(h256 const& _codeHash) const; void mapExecFunc(h256 _codeHash, ExecFunc _funcAddr); + + ExecFunc compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash); }; JITImpl::JITImpl() @@ -145,6 +147,33 @@ void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _funcAddr) m_codeMap.emplace(std::move(_codeHash), _funcAddr); } +ExecFunc JITImpl::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash) +{ + auto name = hash2str(_codeHash); + auto module = Cache::getObject(name); + if (!module) + { + // TODO: Listener support must be redesigned. These should be a feature of JITImpl + //listener->stateChanged(ExecState::Compilation); + assert(_code || !_codeSize); //TODO: Is it good idea to execute empty code? + module = Compiler{{}}.compile(_code, _code + _codeSize, name); + + if (g_optimize) + { + //listener->stateChanged(ExecState::Optimization); + optimize(*module); + } + + prepare(*module); + } + if (g_dump) + module->dump(); + + m_engine->addModule(std::move(module)); + //listener->stateChanged(ExecState::CodeGen); + return (ExecFunc)m_engine->getFunctionAddress(name); +} + } // anonymous namespace bool JIT::isCodeReady(h256 const& _codeHash) @@ -154,60 +183,31 @@ bool JIT::isCodeReady(h256 const& _codeHash) ReturnCode JIT::exec(ExecutionContext& _context) { - auto& jit = JITImpl::instance(); - - std::unique_ptr listener{new ExecStats}; - listener->stateChanged(ExecState::Started); + //std::unique_ptr listener{new ExecStats}; + //listener->stateChanged(ExecState::Started); + //static StatsCollector statsCollector; - auto code = _context.code(); - auto codeSize = _context.codeSize(); + auto& jit = JITImpl::instance(); auto codeHash = _context.codeHash(); - - static StatsCollector statsCollector; - - auto mainFuncName = hash2str(codeHash); - - // TODO: Remove cast auto execFunc = jit.getExecFunc(codeHash); if (!execFunc) { - auto module = Cache::getObject(mainFuncName); - if (!module) - { - listener->stateChanged(ExecState::Compilation); - assert(code || !codeSize); //TODO: Is it good idea to execute empty code? - module = Compiler{{}}.compile(code, code + codeSize, mainFuncName); - - if (g_optimize) - { - listener->stateChanged(ExecState::Optimization); - optimize(*module); - } - - prepare(*module); - } - if (g_dump) - module->dump(); - - jit.engine().addModule(std::move(module)); - listener->stateChanged(ExecState::CodeGen); - execFunc = (ExecFunc)jit.engine().getFunctionAddress(mainFuncName); - if (!CHECK(execFunc)) - return ReturnCode::LLVMLinkError; + execFunc = jit.compile(_context.code(), _context.codeSize(), codeHash); + if (!execFunc) + return ReturnCode::LLVMError; jit.mapExecFunc(codeHash, execFunc); } - listener->stateChanged(ExecState::Execution); + //listener->stateChanged(ExecState::Execution); auto returnCode = execFunc(&_context); - listener->stateChanged(ExecState::Return); + //listener->stateChanged(ExecState::Return); if (returnCode == ReturnCode::Return) _context.returnData = _context.getReturnData(); // Save reference to return data - listener->stateChanged(ExecState::Finished); - - if (g_stats) - statsCollector.stats.push_back(std::move(listener)); + //listener->stateChanged(ExecState::Finished); + // if (g_stats) + // statsCollector.stats.push_back(std::move(listener)); return returnCode; } From 057611fbb36588f6360078d5c1e5eddc318ae0c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Bylica?= Date: Thu, 2 Jul 2015 17:48:32 +0200 Subject: [PATCH 72/72] Extend JIT interface to allow only compilation of EVM code. Make code availability checking thread-safe. --- include/evmjit/JIT.h | 3 +++ libevmjit/JIT.cpp | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/evmjit/JIT.h b/include/evmjit/JIT.h index 8d6f9bbd0..901c351d9 100644 --- a/include/evmjit/JIT.h +++ b/include/evmjit/JIT.h @@ -153,6 +153,9 @@ public: /// \param _codeHash The Keccak hash of the EVM code. EXPORT static bool isCodeReady(h256 const& _codeHash); + /// Compile the given EVM code to machine code and make available for execution. + EXPORT static void compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash); + EXPORT static ReturnCode exec(ExecutionContext& _context); }; diff --git a/libevmjit/JIT.cpp b/libevmjit/JIT.cpp index 89d2e63eb..53c36dcb2 100644 --- a/libevmjit/JIT.cpp +++ b/libevmjit/JIT.cpp @@ -1,6 +1,7 @@ #include "evmjit/JIT.h" #include +#include #include "preprocessor/llvm_includes_start.h" #include @@ -82,6 +83,7 @@ void parseOptions() class JITImpl { std::unique_ptr m_engine; + mutable std::mutex x_codeMap; std::unordered_map m_codeMap; public: @@ -136,6 +138,7 @@ JITImpl::JITImpl() ExecFunc JITImpl::getExecFunc(h256 const& _codeHash) const { + std::lock_guard lock{x_codeMap}; auto it = m_codeMap.find(_codeHash); if (it != m_codeMap.end()) return it->second; @@ -144,6 +147,7 @@ ExecFunc JITImpl::getExecFunc(h256 const& _codeHash) const void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _funcAddr) { + std::lock_guard lock{x_codeMap}; m_codeMap.emplace(std::move(_codeHash), _funcAddr); } @@ -181,6 +185,14 @@ bool JIT::isCodeReady(h256 const& _codeHash) return JITImpl::instance().getExecFunc(_codeHash) != nullptr; } +void JIT::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash) +{ + auto& jit = JITImpl::instance(); + auto execFunc = jit.compile(_code, _codeSize, _codeHash); + if (execFunc) // FIXME: What with error? + jit.mapExecFunc(_codeHash, execFunc); +} + ReturnCode JIT::exec(ExecutionContext& _context) { //std::unique_ptr listener{new ExecStats};