diff --git a/eth/main.cpp b/eth/main.cpp index 2c11e4bf3..b4f0326a9 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -115,6 +115,7 @@ void interactiveHelp() << " reprocess Reprocess a given block." << endl << " dumptrace Dumps a transaction trace" << endl << "to . should be one of pretty, standard, standard+." << endl << " dumpreceipt Dumps a transation receipt." << endl + << " hashrate Print the current hashrate in hashes per second if the client is mining." << endl << " exit Exits the application." << endl; } @@ -800,7 +801,7 @@ int main(int argc, char** argv) { bytes block(8); in.read((char*)block.data(), 8); - block.resize(RLP(block, RLP::LaisezFaire).actualSize()); + block.resize(RLP(block, RLP::LaissezFaire).actualSize()); in.read((char*)block.data() + 8, block.size() - 8); switch (web3.ethereum()->queueBlock(block, safeImport)) @@ -1122,6 +1123,8 @@ int main(int argc, char** argv) cout << "Current block: " << c->blockChain().details().number << endl; else if (c && cmd == "blockqueue") cout << "Current blockqueue status: " << endl << c->blockQueueStatus() << endl; + else if (c && cmd == "hashrate") + cout << "Current hash rate: " << toString(c->hashrate()) << " hashes per second." << endl; else if (c && cmd == "findblock") { if (iss.peek() != -1) diff --git a/evmjit/CMakeLists.txt b/evmjit/CMakeLists.txt index 70d3e1dda..77fb1bf27 100644 --- a/evmjit/CMakeLists.txt +++ b/evmjit/CMakeLists.txt @@ -29,7 +29,3 @@ add_subdirectory(libevmjit) if(EVMJIT_CPP) add_subdirectory(libevmjit-cpp) endif() - -if(EVMJIT_TOOLS) - add_subdirectory(evmcc) -endif() diff --git a/evmjit/evmcc/CMakeLists.txt b/evmjit/evmcc/CMakeLists.txt deleted file mode 100644 index 4ffbf5fb5..000000000 --- a/evmjit/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/evmjit/evmcc/evmcc.cpp b/evmjit/evmcc/evmcc.cpp deleted file mode 100644 index e86f948f2..000000000 --- a/evmjit/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; -} diff --git a/evmjit/include/evmjit/DataTypes.h b/evmjit/include/evmjit/DataTypes.h deleted file mode 100644 index 179d9a372..000000000 --- a/evmjit/include/evmjit/DataTypes.h +++ /dev/null @@ -1,56 +0,0 @@ -#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/evmjit/include/evmjit/JIT.h b/evmjit/include/evmjit/JIT.h index c9ddde705..fcb0db4e7 100644 --- a/evmjit/include/evmjit/JIT.h +++ b/evmjit/include/evmjit/JIT.h @@ -1,19 +1,149 @@ #pragma once -#include "evmjit/DataTypes.h" +#include +#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 evmjit { -namespace jit + +using byte = uint8_t; +using bytes_ref = std::tuple; + +/// Representation of 256-bit hash value +struct h256 { - class ExecutionEngine; -} + uint64_t words[4]; +}; + +inline bool operator==(h256 const& _h1, h256 const& _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]; } -namespace evmjit +/// Representation of 256-bit value binary compatible with LLVM i256 +struct i256 { + uint64_t words[4]; + + i256() = default; + i256(h256 const& _h) { std::memcpy(this, &_h, sizeof(*this)); } +}; + +// TODO: Merge with ExecutionContext +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) + }; + + static size_t const numElements = CodeSize + 1; + + 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; + +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; + uint64_t m_memSize = 0; + uint64_t m_memCap = 0; + +public: + /// Reference to returned data (RETURN opcode used) + bytes_ref returnData; +}; class JIT { @@ -23,14 +153,23 @@ 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); - -private: - friend class dev::eth::jit::ExecutionEngine; + EXPORT static bool isCodeReady(h256 const& _codeHash); - static uint64_t getCode(h256 _codeHash); - static void mapCode(h256 _codeHash, uint64_t _funcAddr); + EXPORT static ReturnCode exec(ExecutionContext& _context); }; } } + +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/evmjit/libevmjit-cpp/Env.cpp b/evmjit/libevmjit-cpp/Env.cpp index 4d7865bd0..3f59765f6 100644 --- a/evmjit/libevmjit-cpp/Env.cpp +++ b/evmjit/libevmjit-cpp/Env.cpp @@ -3,7 +3,6 @@ #include #include #include -#include #include "Utils.h" @@ -21,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 @@ -40,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; @@ -65,7 +64,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) { CallParameters params; - params.value = llvm2eth(*_value); + params.value = jit2eth(*_value); params.senderAddress = _env->myAddress; params.receiveAddress = right160(*_receiveAddress); params.codeAddress = right160(*_codeAddress); diff --git a/evmjit/libevmjit-cpp/JitVM.cpp b/evmjit/libevmjit-cpp/JitVM.cpp index 68161526d..2fb2a0e67 100644 --- a/evmjit/libevmjit-cpp/JitVM.cpp +++ b/evmjit/libevmjit-cpp/JitVM.cpp @@ -7,7 +7,6 @@ #include #include #include -#include #include "Utils.h" @@ -20,8 +19,6 @@ extern "C" void env_sload(); // fake declaration for linker symbol stripping wor bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) { - 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 |= io_gas > std::numeric_limits::max(); // Do not accept requests with gas > 2^63 (int64 max) @@ -40,36 +37,38 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on 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(_ext.codeHash); + m_data.codeHash = eth2jit(_ext.codeHash); - auto env = reinterpret_cast(&_ext); - auto exitCode = m_engine.run(&m_data, env); + // 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) { - case ReturnCode::Suicide: - _ext.suicide(right160(llvm2eth(m_data.address))); + case evmjit::ReturnCode::Suicide: + _ext.suicide(right160(jit2eth(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: @@ -77,7 +76,7 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on } io_gas = m_data.gas; - 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/evmjit/libevmjit-cpp/JitVM.h b/evmjit/libevmjit-cpp/JitVM.h index e97abd83b..43933ebe4 100644 --- a/evmjit/libevmjit-cpp/JitVM.h +++ b/evmjit/libevmjit-cpp/JitVM.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace dev { @@ -14,8 +14,8 @@ public: virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final; private: - jit::RuntimeData m_data; - jit::ExecutionEngine m_engine; + 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/evmjit/libevmjit-cpp/Utils.h b/evmjit/libevmjit-cpp/Utils.h index f9b9b2ef4..ebb6ad97d 100644 --- a/evmjit/libevmjit-cpp/Utils.h +++ b/evmjit/libevmjit-cpp/Utils.h @@ -1,40 +1,41 @@ #pragma once -#include +#include 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; diff --git a/evmjit/libevmjit/Arith256.cpp b/evmjit/libevmjit/Arith256.cpp index 95ca2aee5..76c53c78d 100644 --- a/evmjit/libevmjit/Arith256.cpp +++ b/evmjit/libevmjit/Arith256.cpp @@ -4,6 +4,7 @@ #include #include "preprocessor/llvm_includes_start.h" +#include #include #include "preprocessor/llvm_includes_end.h" @@ -32,186 +33,364 @@ 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(); + 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; +} - auto x = &func->getArgumentList().front(); - x->setName("x"); - auto y = x->getNextNode(); - y->setName("y"); +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); - 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); - } return func; } -llvm::Function* Arith256::getMul512Func() +namespace { - 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(); +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 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); + 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 yLEx = builder.CreateICmpULE(yArg, x); + auto r0 = x; + builder.CreateCondBr(yLEx, 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.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); + + 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); - auto x = &func->getArgumentList().front(); - x->setName("x"); - auto y = x->getNextNode(); - y->setName("y"); + 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) +{ + 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); - 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 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 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::getDivFunc(llvm::Type* _type) +namespace +{ +llvm::Function* createURemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName) { - auto& func = _type == Type::Word ? m_div : m_div512; + auto udivremFunc = _type == Type::Word ? Arith256::getUDivRem256Func(_module) : Arith256::getUDivRem512Func(_module); - 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 + auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module); + func->setDoesNotThrow(); + func->setDoesNotAccessMemory(); - llvm::Type* argTypes[] = {_type, _type}; - auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef{argTypes}); - 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 x = &func->getArgumentList().front(); + x->setName("x"); + auto y = x->getNextNode(); + y->setName("y"); - auto zero = llvm::ConstantInt::get(_type, 0); - auto one = llvm::ConstantInt::get(_type, 1); + 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); - auto x = &func->getArgumentList().front(); - x->setName("x"); - auto yArg = x->getNextNode(); - yArg->setName("y"); + return func; +} +} - InsertPointGuard guard{m_builder}; +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); +} - 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); +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); +} - 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.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); - - 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); +llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module) +{ + static const auto funcName = "evm.sdivrem.i256"; + if (auto func = _module.getFunction(funcName)) + return func; - 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); + 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 remainder 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); - 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.CreateInsertValue(llvm::UndefValue::get(retType), qRet, 0, "ret0"); - ret = m_builder.CreateInsertValue(ret, rRet, 1, "ret"); - m_builder.CreateRet(ret); - } return func; } @@ -260,14 +439,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); @@ -284,137 +464,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 d = createCall(getDivFunc(i512Ty), {s, m512}); - auto r = m_builder.CreateExtractValue(d, 1, "r"); - 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 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); - } - 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) -{ - // 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); -// } -// } - - auto r = createCall(getDivFunc(Type::Word), {_arg1, _arg2}); - auto div = m_builder.CreateExtractValue(r, 0, "div"); - auto mod = m_builder.CreateExtractValue(r, 1, "mod"); - return std::make_pair(div, mod); -} - -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); -// } -// } - - 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) { @@ -445,49 +494,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) -{ - // 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); -// } -// } -// } - - 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); -// } -// } -// } - - return createCall(getMulModFunc(), {_arg1, _arg2, _arg3}); -} - - } } } diff --git a/evmjit/libevmjit/Arith256.h b/evmjit/libevmjit/Arith256.h index 2513ca568..81535a792 100644 --- a/evmjit/libevmjit/Arith256.h +++ b/evmjit/libevmjit/Arith256.h @@ -14,30 +14,25 @@ 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); - 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); + 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* 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; llvm::Function* m_exp = nullptr; - llvm::Function* m_addmod = nullptr; - llvm::Function* m_mulmod = nullptr; llvm::Function* m_debug = nullptr; }; diff --git a/evmjit/libevmjit/Array.cpp b/evmjit/libevmjit/Array.cpp index 9a7f18597..96329afd5 100644 --- a/evmjit/libevmjit/Array.cpp +++ b/evmjit/libevmjit/Array.cpp @@ -6,7 +6,6 @@ #include "preprocessor/llvm_includes_end.h" #include "RuntimeManager.h" -#include "Runtime.h" #include "Utils.h" namespace dev @@ -17,7 +16,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) { @@ -56,7 +54,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/evmjit/libevmjit/BasicBlock.h b/evmjit/libevmjit/BasicBlock.h index 5e19235a7..321499196 100644 --- a/evmjit/libevmjit/BasicBlock.h +++ b/evmjit/libevmjit/BasicBlock.h @@ -11,7 +11,7 @@ namespace eth { namespace jit { - +using namespace evmjit; using instr_idx = uint64_t; class BasicBlock diff --git a/evmjit/libevmjit/CMakeLists.txt b/evmjit/libevmjit/CMakeLists.txt index 4b15d52ac..80108e15b 100644 --- a/evmjit/libevmjit/CMakeLists.txt +++ b/evmjit/libevmjit/CMakeLists.txt @@ -1,26 +1,22 @@ 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 - ExecutionEngine.cpp ExecutionEngine.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 - Runtime.cpp Runtime.h - RuntimeData.h RuntimeManager.cpp RuntimeManager.h Stack.cpp Stack.h Type.cpp Type.h diff --git a/evmjit/libevmjit/Cache.cpp b/evmjit/libevmjit/Cache.cpp index 36dae3763..01b1c8766 100644 --- a/evmjit/libevmjit/Cache.cpp +++ b/evmjit/libevmjit/Cache.cpp @@ -12,15 +12,13 @@ #include #include "preprocessor/llvm_includes_end.h" -#include "ExecutionEngine.h" +#include "ExecStats.h" #include "Utils.h" #include "BuildInfo.gen.h" namespace dev { -namespace eth -{ -namespace jit +namespace evmjit { namespace @@ -29,7 +27,7 @@ namespace std::mutex x_cacheMutex; CacheMode g_mode; std::unique_ptr g_lastObject; - ExecutionEngineListener* g_listener; + JITListener* g_listener; static const size_t c_versionStampLength = 32; llvm::StringRef getLibVersionStamp() @@ -44,15 +42,25 @@ namespace } } -ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener) +ObjectCache* Cache::init(CacheMode _mode, JITListener* _listener) { - static ObjectCache objectCache; - Guard g{x_cacheMutex}; 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() @@ -185,4 +193,3 @@ std::unique_ptr ObjectCache::getObject(llvm::Module const* _ } } -} diff --git a/evmjit/libevmjit/Cache.h b/evmjit/libevmjit/Cache.h index 1d5927705..5de4222fc 100644 --- a/evmjit/libevmjit/Cache.h +++ b/evmjit/libevmjit/Cache.h @@ -14,11 +14,9 @@ namespace llvm namespace dev { -namespace eth +namespace evmjit { -namespace jit -{ -class ExecutionEngineListener; +class JITListener; enum class CacheMode { @@ -47,7 +45,7 @@ public: class Cache { public: - static ObjectCache* getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener); + static ObjectCache* init(CacheMode _mode, JITListener* _listener); static std::unique_ptr getObject(std::string const& id); /// Clears cache storage @@ -59,4 +57,3 @@ public: } } -} diff --git a/evmjit/libevmjit/Common.h b/evmjit/libevmjit/Common.h index b519614fe..4a6d7b4f3 100644 --- a/evmjit/libevmjit/Common.h +++ b/evmjit/libevmjit/Common.h @@ -1,53 +1,16 @@ #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 eth -{ -namespace jit +namespace evmjit { 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, - - UnexpectedException = -111, - - LinkerWorkaround = -299, -}; - #define UNTESTED assert(false) } } -} diff --git a/evmjit/libevmjit/Compiler.cpp b/evmjit/libevmjit/Compiler.cpp index e49979294..a44e4e890 100644 --- a/evmjit/libevmjit/Compiler.cpp +++ b/evmjit/libevmjit/Compiler.cpp @@ -49,6 +49,11 @@ 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) @@ -159,10 +164,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 +183,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(); @@ -270,44 +275,96 @@ 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; } case Instruction::DIV: { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = _arith.div(lhs, rhs); - stack.push(res.first); + 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); break; } case Instruction::SDIV: { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = _arith.sdiv(lhs, rhs); - stack.push(res.first); + 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.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); // protect against undef i256.min / -1 + stack.push(r); break; } case Instruction::MOD: { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = _arith.div(lhs, rhs); - stack.push(res.second); + 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); break; } case Instruction::SMOD: { - auto lhs = stack.pop(); - auto rhs = stack.pop(); - auto res = _arith.sdiv(lhs, rhs); - stack.push(res.second); + 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; + } + + 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; } @@ -417,48 +474,29 @@ 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"); + // 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); - - 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; } - 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(); 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) @@ -495,11 +533,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; } @@ -656,7 +690,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti } case Instruction::CODESIZE: - // TODO: Use constant stack.push(_runtimeManager.getCodeSize()); break; @@ -729,8 +762,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; } @@ -797,7 +830,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti case Instruction::STOP: { - m_builder.CreateRet(Constant::get(ReturnCode::Stop)); + m_builder.CreateBr(m_stopBB); break; } @@ -824,7 +857,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 } } @@ -942,4 +975,3 @@ void Compiler::dump() } } } - diff --git a/evmjit/libevmjit/Compiler.h b/evmjit/libevmjit/Compiler.h index 4469389bb..0b31ae0f2 100644 --- a/evmjit/libevmjit/Compiler.h +++ b/evmjit/libevmjit/Compiler.h @@ -1,6 +1,5 @@ #pragma once -#include "Common.h" #include "BasicBlock.h" namespace dev @@ -65,6 +64,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; diff --git a/evmjit/libevmjit/CompilerHelper.h b/evmjit/libevmjit/CompilerHelper.h index cd6d09a58..5eeae9a6e 100644 --- a/evmjit/libevmjit/CompilerHelper.h +++ b/evmjit/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/evmjit/libevmjit/ExecStats.cpp b/evmjit/libevmjit/ExecStats.cpp index ff8c05307..c7f6ef0cd 100644 --- a/evmjit/libevmjit/ExecStats.cpp +++ b/evmjit/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/evmjit/libevmjit/ExecStats.h b/evmjit/libevmjit/ExecStats.h index 0451ccb05..4a5ae00e1 100644 --- a/evmjit/libevmjit/ExecStats.h +++ b/evmjit/libevmjit/ExecStats.h @@ -1,19 +1,43 @@ #pragma once +#include #include #include #include -#include "ExecutionEngine.h" - namespace dev { -namespace eth +namespace evmjit +{ + +enum class ExecState { -namespace jit + 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() {} -class ExecStats : public ExecutionEngineListener + virtual void stateChanged(ExecState) {} +}; + +class ExecStats : public JITListener { public: using clock = std::chrono::high_resolution_clock; @@ -42,4 +66,3 @@ public: } } -} diff --git a/evmjit/libevmjit/ExecutionEngine.cpp b/evmjit/libevmjit/ExecutionEngine.cpp deleted file mode 100644 index a5b26401f..000000000 --- a/evmjit/libevmjit/ExecutionEngine.cpp +++ /dev/null @@ -1,192 +0,0 @@ -#include "ExecutionEngine.h" - -#include -#include -#include -#include -#include -#include - -#include "preprocessor/llvm_includes_start.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "preprocessor/llvm_includes_end.h" - -#include "evmjit/JIT.h" -#include "Runtime.h" -#include "Compiler.h" -#include "Optimizer.h" -#include "Cache.h" -#include "ExecStats.h" -#include "Utils.h" -#include "BuildInfo.gen.h" - -namespace dev -{ -namespace eth -{ -namespace jit -{ -using evmjit::JIT; - -namespace -{ -using EntryFuncPtr = ReturnCode(*)(Runtime*); - -std::string codeHash(i256 const& _hash) -{ - static const auto size = sizeof(_hash); - static const auto hexChars = "0123456789abcdef"; - std::string str; - str.resize(size * 2); - auto outIt = str.rbegin(); // reverse for BE - auto& arr = *(std::array*)&_hash; - for (auto b : arr) - { - *(outIt++) = hexChars[b & 0xf]; - *(outIt++) = hexChars[b >> 4]; - } - return str; -} - -void printVersion() -{ - std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n" - << " EVMJIT version " << EVMJIT_VERSION << "\n" -#ifdef NDEBUG - << " Optimized build, " EVMJIT_VERSION_FULL "\n" -#else - << " DEBUG build, " EVMJIT_VERSION_FULL "\n" -#endif - << " Built " << __DATE__ << " (" << __TIME__ << ")\n" - << std::endl; -} - -namespace cl = llvm::cl; -cl::opt g_optimize{"O", cl::desc{"Optimize"}}; -cl::opt g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"}, - cl::values( - clEnumValN(CacheMode::on, "1", "Enabled"), - clEnumValN(CacheMode::off, "0", "Disabled"), - clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."), - clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."), - clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."), - clEnumValN(CacheMode::preload, "p", "Preload all cached objects."), - clEnumValEnd)}; -cl::opt g_stats{"st", cl::desc{"Statistics"}}; -cl::opt g_dump{"dump", cl::desc{"Dump LLVM IR module"}}; - -void parseOptions() -{ - static llvm::llvm_shutdown_obj shutdownObj{}; - cl::AddExtraVersionPrinter(printVersion); - cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); -} - -} - - -ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env) -{ - static std::once_flag flag; - std::call_once(flag, parseOptions); - - std::unique_ptr listener{new ExecStats}; - listener->stateChanged(ExecState::Started); - - 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; - - static std::unique_ptr ee; - if (!ee) - { - if (g_cache == CacheMode::clear) - Cache::clear(); - - llvm::InitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - - 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::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; - 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); - - // TODO: Remove cast - auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(_data->codeHash); - if (!entryFuncPtr) - { - auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr; - 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); - - if (g_optimize) - { - listener->stateChanged(ExecState::Optimization); - optimize(*module); - } - } - if (g_dump) - module->dump(); - - ee->addModule(std::move(module)); - listener->stateChanged(ExecState::CodeGen); - entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName); - if (!CHECK(entryFuncPtr)) - return ReturnCode::LLVMLinkError; - JIT::mapCode(_data->codeHash, (uint64_t)entryFuncPtr); // FIXME: Remove cast - } - - listener->stateChanged(ExecState::Execution); - auto returnCode = entryFuncPtr(&m_runtime); - listener->stateChanged(ExecState::Return); - - if (returnCode == ReturnCode::Return) - returnData = m_runtime.getReturnData(); // Save reference to return data - - listener->stateChanged(ExecState::Finished); - - if (g_stats) - statsCollector.stats.push_back(std::move(listener)); - - return returnCode; -} - -} -} -} diff --git a/evmjit/libevmjit/ExecutionEngine.h b/evmjit/libevmjit/ExecutionEngine.h deleted file mode 100644 index 26da6977c..000000000 --- a/evmjit/libevmjit/ExecutionEngine.h +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -#include - -#include "Runtime.h" - -namespace dev -{ -namespace eth -{ -namespace jit -{ - -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: - ExecutionEngine() = default; - ExecutionEngine(ExecutionEngine const&) = delete; - ExecutionEngine& operator=(ExecutionEngine) = delete; - - EXPORT ReturnCode run(RuntimeData* _data, Env* _env); - - /// Reference to returned data (RETURN opcode used) - bytes_ref returnData; - -private: - Runtime m_runtime; -}; - -} -} -} diff --git a/evmjit/libevmjit/Ext.cpp b/evmjit/libevmjit/Ext.cpp index d35aebc68..b8b124ff5 100644 --- a/evmjit/libevmjit/Ext.cpp +++ b/evmjit/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,27 @@ 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); + + m_argCounter = 0; // Release args allocas. TODO: This is a bad design + return Endianness::toNative(m_builder, m_builder.CreateLoad(ret)); } llvm::Value* Ext::balance(llvm::Value* _address) diff --git a/evmjit/libevmjit/Ext.h b/evmjit/libevmjit/Ext.h index b0048d2e9..2af074f4a 100644 --- a/evmjit/libevmjit/Ext.h +++ b/evmjit/libevmjit/Ext.h @@ -35,7 +35,6 @@ enum class EnvFunc log, blockhash, extcode, - calldataload, // Helper function, not client Env interface _size }; @@ -63,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; diff --git a/evmjit/libevmjit/GasMeter.h b/evmjit/libevmjit/GasMeter.h index aecc07315..93a6cc24c 100644 --- a/evmjit/libevmjit/GasMeter.h +++ b/evmjit/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/evmjit/libevmjit/Instruction.cpp b/evmjit/libevmjit/Instruction.cpp index f70b020f8..c2e267cdb 100644 --- a/evmjit/libevmjit/Instruction.cpp +++ b/evmjit/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/evmjit/libevmjit/Instruction.h b/evmjit/libevmjit/Instruction.h index 6785213d6..89add0958 100644 --- a/evmjit/libevmjit/Instruction.h +++ b/evmjit/libevmjit/Instruction.h @@ -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/evmjit/libevmjit/JIT.cpp b/evmjit/libevmjit/JIT.cpp index 8617fab6d..6a9b7f932 100644 --- a/evmjit/libevmjit/JIT.cpp +++ b/evmjit/libevmjit/JIT.cpp @@ -1,45 +1,239 @@ #include "evmjit/JIT.h" -#include +#include + +#include "preprocessor/llvm_includes_start.h" +#include +#include +#include +#include +#include +#include +#include +#include "preprocessor/llvm_includes_end.h" + +#include "Compiler.h" +#include "Optimizer.h" +#include "Cache.h" +#include "ExecStats.h" +#include "Utils.h" +#include "BuildInfo.gen.h" namespace dev { namespace evmjit { +using namespace eth::jit; + namespace { +using ExecFunc = ReturnCode(*)(ExecutionContext*); -class JITImpl: JIT +std::string hash2str(i256 const& _hash) { -public: - std::unordered_map codeMap; + static const auto size = sizeof(_hash); + static const auto hexChars = "0123456789abcdef"; + std::string str; + str.resize(size * 2); + auto outIt = str.rbegin(); // reverse for BE + auto& arr = *(std::array*)&_hash; + for (auto b : arr) + { + *(outIt++) = hexChars[b & 0xf]; + *(outIt++) = hexChars[b >> 4]; + } + return str; +} + +void printVersion() +{ + std::cout << "Ethereum EVM JIT Compiler (http://github.com/ethereum/evmjit):\n" + << " EVMJIT version " << EVMJIT_VERSION << "\n" +#ifdef NDEBUG + << " Optimized build, " EVMJIT_VERSION_FULL "\n" +#else + << " DEBUG build, " EVMJIT_VERSION_FULL "\n" +#endif + << " Built " << __DATE__ << " (" << __TIME__ << ")\n" + << std::endl; +} + +namespace cl = llvm::cl; +cl::opt g_optimize{"O", cl::desc{"Optimize"}}; +cl::opt g_cache{"cache", cl::desc{"Cache compiled EVM code on disk"}, + cl::values( + clEnumValN(CacheMode::on, "1", "Enabled"), + clEnumValN(CacheMode::off, "0", "Disabled"), + clEnumValN(CacheMode::read, "r", "Read only. No new objects are added to cache."), + clEnumValN(CacheMode::write, "w", "Write only. No objects are loaded from cache."), + clEnumValN(CacheMode::clear, "c", "Clear the cache storage. Cache is disabled."), + clEnumValN(CacheMode::preload, "p", "Preload all cached objects."), + clEnumValEnd)}; +cl::opt g_stats{"st", cl::desc{"Statistics"}}; +cl::opt g_dump{"dump", cl::desc{"Dump LLVM IR module"}}; + +void parseOptions() +{ + static llvm::llvm_shutdown_obj shutdownObj{}; + cl::AddExtraVersionPrinter(printVersion); + cl::ParseEnvironmentOptions("evmjit", "EVMJIT", "Ethereum EVM JIT Compiler"); +} + +class JITImpl +{ + 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; + if (preloadCache) + g_cache = CacheMode::on; + + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + + 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::EngineBuilder builder(std::move(module)); + builder.setEngineKind(llvm::EngineKind::JIT); + builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None); + + m_engine.reset(builder.create()); + + // TODO: Update cache listener + m_engine->setObjectCache(Cache::init(g_cache, nullptr)); + + // FIXME: Disabled during API changes + //if (preloadCache) + // Cache::preload(*m_engine, funcCache); +} + +ExecFunc JITImpl::getExecFunc(h256 const& _codeHash) const +{ + auto it = m_codeMap.find(_codeHash); + if (it != m_codeMap.end()) + return it->second; + return nullptr; +} + +void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _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; } -uint64_t JIT::getCode(h256 _codeHash) +ReturnCode JIT::exec(ExecutionContext& _context) { - auto& codeMap = JITImpl::instance().codeMap; - auto it = codeMap.find(_codeHash); - if (it != codeMap.end()) - return it->second; - return 0; + auto& jit = JITImpl::instance(); + + 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 = 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; + jit.mapExecFunc(codeHash, execFunc); + } + + listener->stateChanged(ExecState::Execution); + auto returnCode = execFunc(&_context); + 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)); + + return returnCode; } -void JIT::mapCode(h256 _codeHash, uint64_t _funcAddr) + +extern "C" void ext_free(void* _data) noexcept; + +ExecutionContext::~ExecutionContext() { - JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr)); + 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/evmjit/libevmjit/Memory.cpp b/evmjit/libevmjit/Memory.cpp index 79a82a8ae..fdf5a6b98 100644 --- a/evmjit/libevmjit/Memory.cpp +++ b/evmjit/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" @@ -191,8 +190,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 +233,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/evmjit/libevmjit/Optimizer.cpp b/evmjit/libevmjit/Optimizer.cpp index df88b4df8..dea3ea2a0 100644 --- a/evmjit/libevmjit/Optimizer.cpp +++ b/evmjit/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 @@ -16,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()); @@ -24,6 +29,96 @@ bool optimize(llvm::Module& _module) return pm.run(_module); } +namespace +{ + +class LowerEVMPass: public llvm::BasicBlockPass +{ + static char ID; + +public: + LowerEVMPass(): + llvm::BasicBlockPass(ID) + {} + + virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override; + + using llvm::BasicBlockPass::doFinalization; + 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(); + auto i512Ty = llvm::IntegerType::get(_bb.getContext(), 512); + for (auto it = _bb.begin(); it != _bb.end(); ) + { + auto& inst = *it++; + llvm::Function* func = nullptr; + if (inst.getType() == Type::Word) + { + switch (inst.getOpcode()) + { + case llvm::Instruction::Mul: + func = Arith256::getMulFunc(*module); + break; + + 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; + } + } + else if (inst.getType() == i512Ty) + { + switch (inst.getOpcode()) + { + case llvm::Instruction::URem: + func = Arith256::getURem512Func(*module); + break; + } + } + + if (func) + { + auto call = llvm::CallInst::Create(func, {inst.getOperand(0), inst.getOperand(1)}, "", &inst); + inst.replaceAllUsesWith(call); + inst.eraseFromParent(); + modified = true; + } + } + return modified; +} + +bool LowerEVMPass::doFinalization(llvm::Module&) +{ + return false; +} + +} + +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/evmjit/libevmjit/Optimizer.h b/evmjit/libevmjit/Optimizer.h index 4a3147a7f..4b7ab7e9a 100644 --- a/evmjit/libevmjit/Optimizer.h +++ b/evmjit/libevmjit/Optimizer.h @@ -14,6 +14,8 @@ namespace jit bool optimize(llvm::Module& _module); +bool prepare(llvm::Module& _module); + } } } diff --git a/evmjit/libevmjit/Runtime.cpp b/evmjit/libevmjit/Runtime.cpp deleted file mode 100644 index 7e9a7d52e..000000000 --- a/evmjit/libevmjit/Runtime.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "Runtime.h" - -#include - -namespace dev -{ -namespace eth -{ -namespace jit -{ - -void Runtime::init(RuntimeData* _data, Env* _env) -{ - m_data = _data; - m_env = _env; -} - -extern "C" void ext_free(void* _data) noexcept; - -Runtime::~Runtime() -{ - if (m_memData) - ext_free(m_memData); // Use helper free to check memory leaks -} - -bytes_ref Runtime::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/evmjit/libevmjit/Runtime.h b/evmjit/libevmjit/Runtime.h deleted file mode 100644 index 895128a59..000000000 --- a/evmjit/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/evmjit/libevmjit/RuntimeData.h b/evmjit/libevmjit/RuntimeData.h deleted file mode 100644 index 6a5cd0d14..000000000 --- a/evmjit/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/evmjit/libevmjit/RuntimeManager.cpp b/evmjit/libevmjit/RuntimeManager.cpp index 5c66a5a21..d48b64bb1 100644 --- a/evmjit/libevmjit/RuntimeManager.cpp +++ b/evmjit/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,10 +93,8 @@ 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"); - 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"); @@ -105,6 +103,13 @@ 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))); + + 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(); @@ -180,7 +185,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) @@ -194,8 +199,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); @@ -212,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)); } @@ -265,9 +271,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() @@ -285,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()); } } diff --git a/evmjit/libevmjit/RuntimeManager.h b/evmjit/libevmjit/RuntimeManager.h index c430a1098..8c0728aaf 100644 --- a/evmjit/libevmjit/RuntimeManager.h +++ b/evmjit/libevmjit/RuntimeManager.h @@ -1,8 +1,9 @@ #pragma once +#include + #include "CompilerHelper.h" #include "Type.h" -#include "RuntimeData.h" #include "Instruction.h" namespace dev @@ -11,6 +12,7 @@ namespace eth { namespace jit { +using namespace evmjit; class Stack; class RuntimeManager: public CompilerHelper @@ -61,6 +63,8 @@ private: llvm::Value* m_memPtr = nullptr; llvm::Value* m_envPtr = nullptr; + std::array m_dataElts; + llvm::Value* m_stackSize = nullptr; llvm::Function* m_checkStackLimit = nullptr; diff --git a/evmjit/libevmjit/Stack.cpp b/evmjit/libevmjit/Stack.cpp index 3a686c766..b7b8f25b1 100644 --- a/evmjit/libevmjit/Stack.cpp +++ b/evmjit/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 @@ -23,96 +22,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; @@ -175,32 +84,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" - diff --git a/evmjit/libevmjit/Stack.h b/evmjit/libevmjit/Stack.h index 3c526f0d3..ad10dae12 100644 --- a/evmjit/libevmjit/Stack.h +++ b/evmjit/libevmjit/Stack.h @@ -24,18 +24,10 @@ public: void free() { m_stack.free(); } private: - llvm::Function* getPopFunc(); - llvm::Function* getPushFunc(); llvm::Function* getGetFunc(); - llvm::Function* getSetFunc(); 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; }; diff --git a/evmjit/libevmjit/Type.cpp b/evmjit/libevmjit/Type.cpp index 00a143dea..6ac9a6467 100644 --- a/evmjit/libevmjit/Type.cpp +++ b/evmjit/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/evmjit/libevmjit/Type.h b/evmjit/libevmjit/Type.h index fcca98d74..5e5b9dde5 100644 --- a/evmjit/libevmjit/Type.h +++ b/evmjit/libevmjit/Type.h @@ -4,9 +4,9 @@ #include #include #include -#include "preprocessor/llvm_includes_end.h" // FIXME: LLVM 3.7: check if needed +#include "preprocessor/llvm_includes_end.h" -#include "Common.h" +#include "evmjit/JIT.h" // ReturnCode namespace dev { @@ -14,16 +14,13 @@ namespace eth { namespace jit { +using namespace evmjit; 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; diff --git a/evmjit/libevmjit/interface.cpp b/evmjit/libevmjit/interface.cpp index 01f743a2e..7136f6798 100644 --- a/evmjit/libevmjit/interface.cpp +++ b/evmjit/libevmjit/interface.cpp @@ -1,29 +1,28 @@ -#include "ExecutionEngine.h" +#include "evmjit/JIT.h" extern "C" { +using namespace dev::evmjit; -using namespace dev::eth::jit; - -EXPORT void* evmjit_create() noexcept +EXPORT void* evmjit_create(RuntimeData* _data, Env* _env) noexcept { - // TODO: Make sure ExecutionEngine constructor does not throw - return new(std::nothrow) ExecutionEngine; + if (!_data) + return nullptr; + + // TODO: Make sure ExecutionEngine constructor does not throw + make JIT/ExecutionEngine interface all nothrow + return new(std::nothrow) ExecutionContext{*_data, _env}; } -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) noexcept { - if (!_engine || !_data) - return static_cast(ReturnCode::UnexpectedException); - try { - auto returnCode = _engine->run(_data, _env); + auto returnCode = JIT::exec(*_context); return static_cast(returnCode); } catch(...) diff --git a/libdevcore/RLP.cpp b/libdevcore/RLP.cpp index 330893c76..92d97aec4 100644 --- a/libdevcore/RLP.cpp +++ b/libdevcore/RLP.cpp @@ -320,7 +320,7 @@ std::ostream& dev::operator<<(std::ostream& _out, RLP const& _d) if (_d.isNull()) _out << "null"; else if (_d.isInt()) - _out << std::showbase << std::hex << std::nouppercase << _d.toInt(RLP::LaisezFaire) << dec; + _out << std::showbase << std::hex << std::nouppercase << _d.toInt(RLP::LaissezFaire) << dec; else if (_d.isData()) _out << escaped(_d.toString(), false); else if (_d.isList()) diff --git a/libdevcore/RLP.h b/libdevcore/RLP.h index dee438be4..67f464b9d 100644 --- a/libdevcore/RLP.h +++ b/libdevcore/RLP.h @@ -71,7 +71,7 @@ public: FailIfTooSmall = 16, Strict = ThrowOnFail | FailIfTooBig, VeryStrict = ThrowOnFail | FailIfTooBig | FailIfTooSmall, - LaisezFaire = AllowNonCanon + LaissezFaire = AllowNonCanon }; using Strictness = int; diff --git a/libethereum/EthereumPeer.cpp b/libethereum/EthereumPeer.cpp index 88878334c..eea5d23dc 100644 --- a/libethereum/EthereumPeer.cpp +++ b/libethereum/EthereumPeer.cpp @@ -86,7 +86,7 @@ unsigned EthereumPeer::askOverride() const if (s->info().clientVersion.substr(0, badGeth.size()) == badGeth) return 1; bytes const& d = repMan().data(*s, name()); - return d.empty() ? c_maxBlocksAsk : RLP(d).toInt(RLP::LaisezFaire); + return d.empty() ? c_maxBlocksAsk : RLP(d).toInt(RLP::LaissezFaire); } void EthereumPeer::setRude() diff --git a/libevm/SmartVM.cpp b/libevm/SmartVM.cpp index 12f2f7078..f8785b6df 100644 --- a/libevm/SmartVM.cpp +++ b/libevm/SmartVM.cpp @@ -47,7 +47,7 @@ bytesConstRef SmartVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _ auto vmKind = VMKind::Interpreter; // default VM // Jitted EVM code already in memory? - if (evmjit::JIT::isCodeReady(eth2llvm(codeHash))) + if (evmjit::JIT::isCodeReady(eth2jit(codeHash))) { cnote << "Jitted"; vmKind = VMKind::JIT; diff --git a/rlp/main.cpp b/rlp/main.cpp index 400a0cb70..8ff7202c3 100644 --- a/rlp/main.cpp +++ b/rlp/main.cpp @@ -113,9 +113,9 @@ public: m_out << "null"; else if (_d.isInt() && !m_prefs.stringInts) if (m_prefs.hexInts) - m_out << (m_prefs.hexPrefix ? "0x" : "") << toHex(toCompactBigEndian(_d.toInt(RLP::LaisezFaire), 1), 1); + m_out << (m_prefs.hexPrefix ? "0x" : "") << toHex(toCompactBigEndian(_d.toInt(RLP::LaissezFaire), 1), 1); else - m_out << _d.toInt(RLP::LaisezFaire); + m_out << _d.toInt(RLP::LaissezFaire); else if (_d.isData() || (_d.isInt() && m_prefs.stringInts)) if (m_prefs.forceString || (!m_prefs.forceHex && isAscii(_d.toString()))) m_out << escaped(_d.toString(), m_prefs.escapeAll); diff --git a/test/libwhisper/bloomFilter.cpp b/test/libwhisper/bloomFilter.cpp index 814990d52..299fd3f5b 100644 --- a/test/libwhisper/bloomFilter.cpp +++ b/test/libwhisper/bloomFilter.cpp @@ -244,4 +244,55 @@ BOOST_AUTO_TEST_CASE(bloomFilterRaw) BOOST_REQUIRE(!f.contains(b00110111)); } +static const unsigned DistributionTestSize = 8; +static const unsigned TestArrSize = 8 * DistributionTestSize; + +void updateDistribution(FixedHash const& _h, array& _distribution) +{ + unsigned bits = 0; + for (unsigned i = 0; i < DistributionTestSize; ++i) + if (_h[i]) + for (unsigned j = 0; j < 8; ++j) + if (_h[i] & c_powerOfTwoBitMmask[j]) + { + _distribution[i * 8 + j]++; + if (++bits >= TopicBloomFilterTest::BitsPerBloom) + return; + } +} + +BOOST_AUTO_TEST_CASE(distributionRate) +{ + cnote << "Testing Bloom Filter Distribution Rate..."; + + array distribution; + for (unsigned i = 0; i < TestArrSize; ++i) + distribution[i] = 0; + + Topic x(0xC0FFEE); // deterministic pseudorandom value + + for (unsigned i = 0; i < 22000; ++i) + { + x = sha3(x); + FixedHash h = x.template bloomPart(); + updateDistribution(h, distribution); + } + + unsigned average = 0; + for (unsigned i = 0; i < TestArrSize; ++i) + average += distribution[i]; + + average /= TestArrSize; + unsigned deviation = average / 10; // approx. 10% + unsigned maxAllowed = average + deviation; + unsigned minAllowed = average - deviation; + + for (unsigned i = 0; i < TestArrSize; ++i) + { + //cnote << i << ":" << distribution[i]; + BOOST_REQUIRE(distribution[i] > minAllowed); + BOOST_REQUIRE(distribution[i] < maxAllowed); + } +} + BOOST_AUTO_TEST_SUITE_END()