Browse Source

Merge branch 'update' into develop

cl-refactor
Paweł Bylica 10 years ago
parent
commit
5d757b198c
  1. 31
      CMakeLists.txt
  2. 18
      evmcc/CMakeLists.txt
  3. 210
      evmcc/evmcc.cpp
  4. 176
      include/evmjit/JIT.h
  5. 3
      libevmjit-cpp/CMakeLists.txt
  6. 51
      libevmjit-cpp/Env.cpp
  7. 61
      libevmjit-cpp/JitVM.cpp
  8. 12
      libevmjit-cpp/JitVM.h
  9. 35
      libevmjit-cpp/Utils.h
  10. 685
      libevmjit/Arith256.cpp
  11. 27
      libevmjit/Arith256.h
  12. 66
      libevmjit/Array.cpp
  13. 2
      libevmjit/BasicBlock.h
  14. 7
      libevmjit/CMakeLists.txt
  15. 69
      libevmjit/Cache.cpp
  16. 15
      libevmjit/Cache.h
  17. 49
      libevmjit/Common.h
  18. 162
      libevmjit/Compiler.cpp
  19. 4
      libevmjit/Compiler.h
  20. 25
      libevmjit/CompilerHelper.h
  21. 5
      libevmjit/Endianness.cpp
  22. 5
      libevmjit/ExecStats.cpp
  23. 35
      libevmjit/ExecStats.h
  24. 210
      libevmjit/ExecutionEngine.cpp
  25. 59
      libevmjit/ExecutionEngine.h
  26. 24
      libevmjit/Ext.cpp
  27. 2
      libevmjit/Ext.h
  28. 22
      libevmjit/GasMeter.cpp
  29. 1
      libevmjit/GasMeter.h
  30. 5
      libevmjit/Instruction.cpp
  31. 5
      libevmjit/Instruction.h
  32. 252
      libevmjit/JIT.cpp
  33. 6
      libevmjit/Memory.cpp
  34. 103
      libevmjit/Optimizer.cpp
  35. 2
      libevmjit/Optimizer.h
  36. 43
      libevmjit/Runtime.cpp
  37. 30
      libevmjit/Runtime.h
  38. 60
      libevmjit/RuntimeData.h
  39. 64
      libevmjit/RuntimeManager.cpp
  40. 6
      libevmjit/RuntimeManager.h
  41. 120
      libevmjit/Stack.cpp
  42. 8
      libevmjit/Stack.h
  43. 3
      libevmjit/Type.cpp
  44. 10
      libevmjit/Type.h
  45. 25
      libevmjit/interface.cpp

31
CMakeLists.txt

@ -7,38 +7,25 @@ set(CMAKE_AUTOMOC OFF)
if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC") if(${CMAKE_CXX_COMPILER_ID} STREQUAL "MSVC")
else() else()
set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas") set(CMAKE_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wconversion -Wno-sign-conversion -Wno-unknown-pragmas ${CMAKE_CXX_FLAGS}")
endif() endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux" AND NOT ${CMAKE_BUILD_TYPE} STREQUAL "DebugSan")
# Do not allow unresovled symbols in shared library (default on linux) # Do not allow unresovled symbols in shared library (default on linux)
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined") set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--no-undefined")
endif() endif()
# LLVM # LLVM
if(LLVM_DIR OR APPLE) # local LLVM build find_package(LLVM 3.7 REQUIRED CONFIG)
find_package(LLVM REQUIRED CONFIG) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}")
message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") add_definitions(${LLVM_DEFINITIONS})
add_definitions(${LLVM_DEFINITIONS}) llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen ipo)
# TODO: bitwriter is needed only for evmcc
llvm_map_components_to_libnames(LLVM_LIBS core support mcjit x86asmparser x86codegen bitwriter ipo) get_filename_component(EVMJIT_INCLUDE_DIR include ABSOLUTE)
else()
# Workaround for Ubuntu broken LLVM package
message(STATUS "Using llvm-3.5-dev package from Ubuntu. If does not work, build LLVM and set -DLLVM_DIR=llvm-build/share/llvm/cmake")
execute_process(COMMAND llvm-config-3.5 --includedir OUTPUT_VARIABLE LLVM_INCLUDE_DIRS)
message(STATUS "LLVM include dirs: ${LLVM_INCLUDE_DIRS}")
set(LLVM_LIBS "-lLLVMBitWriter -lLLVMX86CodeGen -lLLVMSelectionDAG -lLLVMAsmPrinter -lLLVMCodeGen -lLLVMScalarOpts -lLLVMInstCombine -lLLVMTransformUtils -lLLVMipa -lLLVMAnalysis -lLLVMX86AsmParser -lLLVMX86Desc -lLLVMX86Info -lLLVMX86AsmPrinter -lLLVMX86Utils -lLLVMMCJIT -lLLVMTarget -lLLVMRuntimeDyld -lLLVMObject -lLLVMMCParser -lLLVMBitReader -lLLVMExecutionEngine -lLLVMMC -lLLVMCore -lLLVMSupport -lz -lpthread -lffi -ltinfo -ldl -lm")
add_definitions(-D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS)
link_directories(/usr/lib/llvm-3.5/lib)
endif()
add_subdirectory(libevmjit) add_subdirectory(libevmjit)
if(EVMJIT_CPP) if(EVMJIT_CPP)
add_subdirectory(libevmjit-cpp) add_subdirectory(libevmjit-cpp)
endif() endif()
if(EVMJIT_TOOLS)
add_subdirectory(evmcc)
endif()

18
evmcc/CMakeLists.txt

@ -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 )

210
evmcc/evmcc.cpp

@ -1,210 +0,0 @@
#include <chrono>
#include <iostream>
#include <fstream>
#include <ostream>
#include <string>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/program_options.hpp>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/Support/raw_os_ostream.h>
#include <llvm/Support/Signals.h>
#include <llvm/Support/PrettyStackTrace.h>
#include <libdevcore/Common.h>
#include <libdevcore/CommonIO.h>
#include <libevmcore/Instruction.h>
#include <libevm/ExtVMFace.h>
#include <evmjit/libevmjit/Compiler.h>
#include <evmjit/libevmjit/ExecutionEngine.h>
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<size_t>(), "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<std::string>(), "dump generated LLVM IR to file")
("output-bc", opt::value<std::string>(), "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<std::string>(), "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] << " <options> 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::string>();
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<char>(ifs)),
(std::istreambuf_iterator<char>()));
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<size_t>();
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::string>();
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::string>();
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<std::chrono::microseconds>(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<int>(result);
}
}
return 0;
}

176
include/evmjit/JIT.h

@ -0,0 +1,176 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <functional>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
namespace dev
{
namespace evmjit
{
using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
/// Representation of 256-bit hash value
struct h256
{
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];
}
/// 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
LLVMError = -101,
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
{
public:
/// Ask JIT if the EVM code is ready for execution.
/// Returns `true` if the EVM code has been compiled and loaded into memory.
/// In this case the code can be executed without overhead.
/// \param _codeHash The Keccak hash of the EVM code.
EXPORT static bool isCodeReady(h256 const& _codeHash);
/// Compile the given EVM code to machine code and make available for execution.
EXPORT static void compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash);
EXPORT static ReturnCode exec(ExecutionContext& _context);
};
}
}
namespace std
{
template<> struct hash<dev::evmjit::h256>
{
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<size_t>(_h.words[0]);
};
};
}

3
libevmjit-cpp/CMakeLists.txt

@ -15,10 +15,11 @@ else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # add PIC for archive set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") # add PIC for archive
endif() endif()
add_library(${TARGET_NAME} ${SOURCES}) add_library(${TARGET_NAME} STATIC ${SOURCES})
set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs") set_property(TARGET ${TARGET_NAME} PROPERTY FOLDER "libs")
include_directories(../..) include_directories(../..)
include_directories(${EVMJIT_INCLUDE_DIR})
include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${Boost_INCLUDE_DIRS}) include_directories(${Boost_INCLUDE_DIRS})

51
libevmjit-cpp/Env.cpp

@ -1,7 +1,7 @@
#pragma GCC diagnostic ignored "-Wconversion" #pragma GCC diagnostic ignored "-Wconversion"
#include <libdevcrypto/SHA3.h> #include <libdevcore/SHA3.h>
#include <libethcore/Params.h> #include <libevmcore/Params.h>
#include <libevm/ExtVMFace.h> #include <libevm/ExtVMFace.h>
#include "Utils.h" #include "Utils.h"
@ -16,19 +16,19 @@ extern "C"
using namespace dev; using namespace dev;
using namespace dev::eth; using namespace dev::eth;
using jit::i256; using evmjit::i256;
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value) 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 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) EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value)
{ {
auto index = llvm2eth(*_index); auto index = jit2eth(*_index);
auto value = llvm2eth(*_value); auto value = jit2eth(*_value);
if (value == 0 && _env->store(index) != 0) // If delete if (value == 0 && _env->store(index) != 0) // If delete
_env->sub.refunds += c_sstoreRefundGas; // Increase refund counter _env->sub.refunds += c_sstoreRefundGas; // Increase refund counter
@ -39,21 +39,21 @@ extern "C"
EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value) EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value)
{ {
auto u = _env->balance(right160(*_address)); 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) 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) 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) if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{ {
u256 gas = *io_gas; u256 gas = *io_gas;
h256 address(_env->create(endowment, gas, {_initBeg, _initSize}, {}), h256::AlignRight); h256 address(_env->create(endowment, gas, {_initBeg, (size_t)_initSize}, {}), h256::AlignRight);
*io_gas = static_cast<int64_t>(gas); *io_gas = static_cast<int64_t>(gas);
*o_address = address; *o_address = address;
} }
@ -63,19 +63,24 @@ extern "C"
EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress) EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
{ {
auto value = llvm2eth(*_value); CallParameters params;
auto receiveAddress = right160(*_receiveAddress); params.value = jit2eth(*_value);
auto codeAddress = right160(*_codeAddress); params.senderAddress = _env->myAddress;
const auto isCall = receiveAddress == codeAddress; // OPT: The same address pointer can be used if not CODECALL params.receiveAddress = right160(*_receiveAddress);
params.codeAddress = right160(*_codeAddress);
params.data = {_inBeg, (size_t)_inSize};
params.out = {_outBeg, (size_t)_outSize};
params.onOp = {};
const auto isCall = params.receiveAddress == params.codeAddress; // OPT: The same address pointer can be used if not CODECALL
*io_gas -= _callGas; *io_gas -= _callGas;
if (*io_gas < 0) if (*io_gas < 0)
return false; return false;
if (isCall && !_env->exists(receiveAddress)) if (isCall && !_env->exists(params.receiveAddress))
*io_gas -= static_cast<int64_t>(c_callNewAccountGas); // no underflow, *io_gas non-negative before *io_gas -= static_cast<int64_t>(c_callNewAccountGas); // no underflow, *io_gas non-negative before
if (value > 0) // value transfer if (params.value > 0) // value transfer
{ {
/*static*/ assert(c_callValueTransferGas > c_callStipend && "Overflow possible"); /*static*/ assert(c_callValueTransferGas > c_callStipend && "Overflow possible");
*io_gas -= static_cast<int64_t>(c_callValueTransferGas); // no underflow *io_gas -= static_cast<int64_t>(c_callValueTransferGas); // no underflow
@ -86,17 +91,17 @@ extern "C"
return false; return false;
auto ret = false; auto ret = false;
auto callGas = u256{_callGas}; params.gas = u256{_callGas};
if (_env->balance(_env->myAddress) >= value && _env->depth < 1024) if (_env->balance(_env->myAddress) >= params.value && _env->depth < 1024)
ret = _env->call(receiveAddress, value, {_inBeg, _inSize}, callGas, {_outBeg, _outSize}, {}, {}, codeAddress); ret = _env->call(params);
*io_gas += static_cast<int64_t>(callGas); // it is never more than initial _callGas *io_gas += static_cast<int64_t>(params.gas); // it is never more than initial _callGas
return ret; return ret;
} }
EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash) EXPORT void env_sha3(byte* _begin, uint64_t _size, h256* o_hash)
{ {
auto hash = sha3({_begin, _size}); auto hash = sha3({_begin, (size_t)_size});
*o_hash = hash; *o_hash = hash;
} }
@ -124,7 +129,7 @@ extern "C"
if (_topic4) if (_topic4)
topics.push_back(*_topic4); topics.push_back(*_topic4);
_env->log(std::move(topics), {_beg, _size}); _env->log(std::move(topics), {_beg, (size_t)_size});
} }
} }

61
libevmjit-cpp/JitVM.cpp

@ -4,10 +4,9 @@
#include "JitVM.h" #include "JitVM.h"
#include <libdevcore/Log.h> #include <libdevcore/Log.h>
#include <libdevcrypto/SHA3.h> #include <libdevcore/SHA3.h>
#include <libevm/VM.h> #include <libevm/VM.h>
#include <libevm/VMFactory.h> #include <libevm/VMFactory.h>
#include <evmjit/libevmjit/ExecutionEngine.h>
#include "Utils.h" #include "Utils.h"
@ -18,70 +17,66 @@ namespace eth
extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below extern "C" void env_sload(); // fake declaration for linker symbol stripping workaround, see a call below
bytesConstRef JitVM::go(ExtVMFace& _ext, OnOpFunc const& _onOp, uint64_t _step) bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{ {
using namespace jit;
auto rejected = false; auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope // TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= m_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max) rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max(); rejected |= _ext.gasPrice > std::numeric_limits<decltype(m_data.gasPrice)>::max();
rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max(); rejected |= _ext.currentBlock.number > std::numeric_limits<decltype(m_data.number)>::max();
rejected |= _ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max(); rejected |= _ext.currentBlock.timestamp > std::numeric_limits<decltype(m_data.timestamp)>::max();
if (rejected) if (rejected)
{ {
cwarn << "Execution rejected by EVM JIT (gas limit: " << m_gas << "), executing with interpreter"; cwarn << "Execution rejected by EVM JIT (gas limit: " << io_gas << "), executing with interpreter";
VMFactory::setKind(VMKind::Interpreter); m_fallbackVM = VMFactory::create(VMKind::Interpreter);
m_fallbackVM = VMFactory::create(m_gas); return m_fallbackVM->execImpl(io_gas, _ext, _onOp);
VMFactory::setKind(VMKind::JIT);
auto&& output = m_fallbackVM->go(_ext, _onOp, _step);
m_gas = m_fallbackVM->gas(); // copy remaining gas, Executive expects it
return output;
} }
m_data.gas = static_cast<decltype(m_data.gas)>(m_gas); m_data.gas = static_cast<decltype(m_data.gas)>(io_gas);
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice); m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
m_data.callData = _ext.data.data(); m_data.callData = _ext.data.data();
m_data.callDataSize = _ext.data.size(); m_data.callDataSize = _ext.data.size();
m_data.address = eth2llvm(fromAddress(_ext.myAddress)); m_data.address = eth2jit(fromAddress(_ext.myAddress));
m_data.caller = eth2llvm(fromAddress(_ext.caller)); m_data.caller = eth2jit(fromAddress(_ext.caller));
m_data.origin = eth2llvm(fromAddress(_ext.origin)); m_data.origin = eth2jit(fromAddress(_ext.origin));
m_data.callValue = eth2llvm(_ext.value); m_data.callValue = eth2jit(_ext.value);
m_data.coinBase = eth2llvm(fromAddress(_ext.currentBlock.coinbaseAddress)); m_data.coinBase = eth2jit(fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.difficulty = eth2llvm(_ext.currentBlock.difficulty); m_data.difficulty = eth2jit(_ext.currentBlock.difficulty);
m_data.gasLimit = eth2llvm(_ext.currentBlock.gasLimit); m_data.gasLimit = eth2jit(_ext.currentBlock.gasLimit);
m_data.number = static_cast<decltype(m_data.number)>(_ext.currentBlock.number); m_data.number = static_cast<decltype(m_data.number)>(_ext.currentBlock.number);
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp); m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp);
m_data.code = _ext.code.data(); m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size(); m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2llvm(sha3(_ext.code)); m_data.codeHash = eth2jit(_ext.codeHash);
auto env = reinterpret_cast<Env*>(&_ext); // Pass pointer to ExtVMFace casted to evmjit::Env* opaque type.
auto exitCode = m_engine.run(&m_data, env); // 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<evmjit::Env*>(&_ext));
auto exitCode = evmjit::JIT::exec(m_context);
switch (exitCode) switch (exitCode)
{ {
case ReturnCode::Suicide: case evmjit::ReturnCode::Suicide:
_ext.suicide(right160(llvm2eth(m_data.address))); _ext.suicide(right160(jit2eth(m_data.address)));
break; break;
case ReturnCode::BadJumpDestination: case evmjit::ReturnCode::BadJumpDestination:
BOOST_THROW_EXCEPTION(BadJumpDestination()); BOOST_THROW_EXCEPTION(BadJumpDestination());
case ReturnCode::OutOfGas: case evmjit::ReturnCode::OutOfGas:
BOOST_THROW_EXCEPTION(OutOfGas()); BOOST_THROW_EXCEPTION(OutOfGas());
case ReturnCode::StackUnderflow: case evmjit::ReturnCode::StackUnderflow: // FIXME: Remove support for detail errors
BOOST_THROW_EXCEPTION(StackUnderflow()); BOOST_THROW_EXCEPTION(StackUnderflow());
case ReturnCode::BadInstruction: case evmjit::ReturnCode::BadInstruction:
BOOST_THROW_EXCEPTION(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 env_sload(); // but forces linker to include env_* JIT callback functions
break; break;
default: default:
break; break;
} }
m_gas = m_data.gas; // TODO: Remove m_gas field io_gas = m_data.gas;
return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)}; return {std::get<0>(m_context.returnData), std::get<1>(m_context.returnData)};
} }
} }

12
libevmjit-cpp/JitVM.h

@ -1,7 +1,7 @@
#pragma once #pragma once
#include <libevm/VMFace.h> #include <libevm/VMFace.h>
#include <evmjit/libevmjit/ExecutionEngine.h> #include <evmjit/JIT.h>
namespace dev namespace dev
{ {
@ -10,14 +10,12 @@ namespace eth
class JitVM: public VMFace class JitVM: public VMFace
{ {
virtual bytesConstRef go(ExtVMFace& _ext, OnOpFunc const& _onOp = {}, uint64_t _steps = (uint64_t)-1) override final; public:
virtual bytesConstRef execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp) override final;
private: private:
friend class VMFactory; evmjit::RuntimeData m_data;
explicit JitVM(u256 _gas = 0) : VMFace(_gas) {} evmjit::ExecutionContext m_context;
jit::RuntimeData m_data;
jit::ExecutionEngine m_engine;
std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT std::unique_ptr<VMFace> m_fallbackVM; ///< VM used in case of input data rejected by JIT
}; };

35
libevmjit-cpp/Utils.h

@ -1,38 +1,45 @@
#pragma once #pragma once
#include <evmjit/libevmjit/Common.h> #include <evmjit/JIT.h>
namespace dev namespace dev
{ {
namespace eth namespace eth
{ {
inline u256 llvm2eth(jit::i256 _i) /// Converts EVM JIT representation of 256-bit integer to eth type dev::u256.
inline u256 jit2eth(evmjit::i256 _i)
{ {
u256 u = 0; u256 u = _i.words[3];
u |= _i.d;
u <<= 64; u <<= 64;
u |= _i.c; u |= _i.words[2];
u <<= 64; u <<= 64;
u |= _i.b; u |= _i.words[1];
u <<= 64; u <<= 64;
u |= _i.a; u |= _i.words[0];
return u; return u;
} }
inline jit::i256 eth2llvm(u256 _u) /// Converts eth type dev::u256 to EVM JIT representation of 256-bit integer.
inline evmjit::i256 eth2jit(u256 _u)
{ {
jit::i256 i; evmjit::i256 i;
u256 mask = 0xFFFFFFFFFFFFFFFF; i.words[0] = static_cast<uint64_t>(_u);
i.a = static_cast<uint64_t>(_u & mask);
_u >>= 64; _u >>= 64;
i.b = static_cast<uint64_t>(_u & mask); i.words[1] = static_cast<uint64_t>(_u);
_u >>= 64; _u >>= 64;
i.c = static_cast<uint64_t>(_u & mask); i.words[2] = static_cast<uint64_t>(_u);
_u >>= 64; _u >>= 64;
i.d = static_cast<uint64_t>(_u & mask); i.words[3] = static_cast<uint64_t>(_u);
return i; return i;
} }
/// 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;
}
} }
} }

685
libevmjit/Arith256.cpp

@ -4,6 +4,7 @@
#include <iomanip> #include <iomanip>
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.h> #include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
@ -32,197 +33,364 @@ void Arith256::debug(llvm::Value* _value, char _c)
createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_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; static const auto funcName = "evm.mul.i256";
if (!func) if (auto func = _module.getFunction(funcName))
{ return func;
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule()); llvm::Type* argTypes[] = {Type::Word, Type::Word};
func->setDoesNotThrow(); auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotAccessMemory(); 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(); llvm::Function* Arith256::getMul512Func(llvm::Module& _module)
x->setName("x"); {
auto y = x->getNextNode(); static const auto funcName = "evm.mul.i512";
y->setName("y"); 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; return func;
} }
llvm::Function* Arith256::getMul512Func() namespace
{ {
auto& func = m_mul512; llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName)
if (!func) {
{ // Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research
auto i512 = m_builder.getIntNTy(512); // The following algorithm also handles divisor of value 0 returning 0 for both quotient and remainder
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule()); auto retType = llvm::VectorType::get(_type, 2);
func->setDoesNotThrow(); auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
func->setDoesNotAccessMemory(); 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(); return func;
x->setName("x"); }
auto y = x->getNextNode(); }
y->setName("y");
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; 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) auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
{ func->setDoesNotThrow();
// Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research func->setDoesNotAccessMemory();
// The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder
llvm::Type* argTypes[] = {_type, _type}; auto x = &func->getArgumentList().front();
auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef<llvm::Type*>{argTypes}); x->setName("x");
auto funcName = _type == Type::Word ? "div" : "div512"; auto y = x->getNextNode();
func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule()); y->setName("y");
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto zero = llvm::ConstantInt::get(_type, 0); auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto one = llvm::ConstantInt::get(_type, 1); 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(); return func;
x->setName("x"); }
auto yArg = x->getNextNode(); }
yArg->setName("y");
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); llvm::Function* Arith256::getURem512Func(llvm::Module& _module)
auto mainBB = llvm::BasicBlock::Create(m_builder.getContext(), "Main", func); {
auto loopBB = llvm::BasicBlock::Create(m_builder.getContext(), "Loop", func); static const auto funcName = "evm.urem.i512";
auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", func); if (auto func = _module.getFunction(funcName))
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func); return func;
return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName);
}
m_builder.SetInsertPoint(entryBB); llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module)
auto yNonZero = m_builder.CreateICmpNE(yArg, zero); {
auto yLEx = m_builder.CreateICmpULE(yArg, x); static const auto funcName = "evm.sdivrem.i256";
auto r0 = m_builder.CreateSelect(yNonZero, x, zero, "r0"); if (auto func = _module.getFunction(funcName))
m_builder.CreateCondBr(m_builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB); return func;
m_builder.SetInsertPoint(mainBB);
auto ctlzIntr = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, _type);
// both y and r are non-zero
auto yLz = m_builder.CreateCall2(ctlzIntr, yArg, m_builder.getInt1(true), "y.lz");
auto rLz = m_builder.CreateCall2(ctlzIntr, r0, m_builder.getInt1(true), "r.lz");
auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0");
auto shlBy0 = m_builder.CreateICmpEQ(i0, zero);
auto y0 = m_builder.CreateShl(yArg, i0);
if (_type == m_builder.getIntNTy(512)) // Workaround for shl bug for long shifts
{
const auto treshold = m_builder.getIntN(512, 128);
auto highShift = m_builder.CreateICmpUGT(i0, treshold);
auto s = m_builder.CreateNUWSub(i0, treshold);
auto yhs = m_builder.CreateShl(yArg, treshold);
yhs = m_builder.CreateShl(yhs, s);
y0 = m_builder.CreateSelect(highShift, yhs, y0);
}
y0 = m_builder.CreateSelect(shlBy0, yArg, y0, "y0"); // Workaround for LLVM bug: shl by 0 produces wrong result
m_builder.CreateBr(loopBB);
m_builder.SetInsertPoint(loopBB);
auto yPhi = m_builder.CreatePHI(_type, 2, "y.phi");
auto rPhi = m_builder.CreatePHI(_type, 2, "r.phi");
auto iPhi = m_builder.CreatePHI(_type, 2, "i.phi");
auto qPhi = m_builder.CreatePHI(_type, 2, "q.phi");
auto rUpdate = m_builder.CreateNUWSub(rPhi, yPhi);
auto qUpdate = m_builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0
auto rGEy = m_builder.CreateICmpUGE(rPhi, yPhi);
auto r1 = m_builder.CreateSelect(rGEy, rUpdate, rPhi, "r1");
auto q1 = m_builder.CreateSelect(rGEy, qUpdate, qPhi, "q");
auto iZero = m_builder.CreateICmpEQ(iPhi, zero);
m_builder.CreateCondBr(iZero, returnBB, continueBB);
m_builder.SetInsertPoint(continueBB); auto udivremFunc = getUDivRem256Func(_module);
auto i2 = m_builder.CreateNUWSub(iPhi, one);
auto q2 = m_builder.CreateShl(q1, one); auto retType = llvm::VectorType::get(Type::Word, 2);
auto y2 = m_builder.CreateLShr(yPhi, one); auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
m_builder.CreateBr(loopBB); func->setDoesNotThrow();
func->setDoesNotAccessMemory();
yPhi->addIncoming(y0, mainBB);
yPhi->addIncoming(y2, continueBB); auto x = &func->getArgumentList().front();
rPhi->addIncoming(r0, mainBB); x->setName("x");
rPhi->addIncoming(r1, continueBB); auto y = x->getNextNode();
iPhi->addIncoming(i0, mainBB); y->setName("y");
iPhi->addIncoming(i2, continueBB);
qPhi->addIncoming(zero, mainBB); auto bb = llvm::BasicBlock::Create(_module.getContext(), "", func);
qPhi->addIncoming(q2, continueBB); 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; return func;
} }
@ -271,14 +439,15 @@ llvm::Function* Arith256::getExpFunc()
m_builder.CreateCondBr(eOdd, updateBB, continueBB); m_builder.CreateCondBr(eOdd, updateBB, continueBB);
m_builder.SetInsertPoint(updateBB); 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.CreateBr(continueBB);
m_builder.SetInsertPoint(continueBB); m_builder.SetInsertPoint(continueBB);
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1"); auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1");
r1->addIncoming(r, bodyBB); r1->addIncoming(r, bodyBB);
r1->addIncoming(r0, updateBB); 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"); auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1");
m_builder.CreateBr(headerBB); m_builder.CreateBr(headerBB);
@ -295,137 +464,6 @@ llvm::Function* Arith256::getExpFunc()
return m_exp; 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<llvm::ConstantInt>(_arg1))
{
if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
return Constant::get(c1->getValue() * c2->getValue());
}
return createCall(getMulFunc(), {_arg1, _arg2});
}
std::pair<llvm::Value*, llvm::Value*> Arith256::div(llvm::Value* _arg1, llvm::Value* _arg2)
{
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_arg1))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_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<llvm::Value*, llvm::Value*> Arith256::sdiv(llvm::Value* _x, llvm::Value* _y)
{
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<llvm::ConstantInt>(_x))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_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) llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2)
{ {
// while (e != 0) { // while (e != 0) {
@ -456,49 +494,6 @@ llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2)
return createCall(getExpFunc(), {_arg1, _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<llvm::ConstantInt>(_arg1))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
// {
// if (auto c3 = llvm::dyn_cast<llvm::ConstantInt>(_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<llvm::ConstantInt>(_arg1))
// {
// if (auto c2 = llvm::dyn_cast<llvm::ConstantInt>(_arg2))
// {
// if (auto c3 = llvm::dyn_cast<llvm::ConstantInt>(_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});
}
} }
} }
} }

27
libevmjit/Arith256.h

@ -14,30 +14,25 @@ class Arith256 : public CompilerHelper
public: public:
Arith256(llvm::IRBuilder<>& _builder); Arith256(llvm::IRBuilder<>& _builder);
llvm::Value* mul(llvm::Value* _arg1, llvm::Value* _arg2);
std::pair<llvm::Value*, llvm::Value*> div(llvm::Value* _arg1, llvm::Value* _arg2);
std::pair<llvm::Value*, llvm::Value*> sdiv(llvm::Value* _arg1, llvm::Value* _arg2);
llvm::Value* exp(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); 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: private:
llvm::Function* getMulFunc();
llvm::Function* getMul512Func();
llvm::Function* getDivFunc(llvm::Type* _type);
llvm::Function* getExpFunc(); 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_exp = nullptr;
llvm::Function* m_addmod = nullptr;
llvm::Function* m_mulmod = nullptr;
llvm::Function* m_debug = nullptr; llvm::Function* m_debug = nullptr;
}; };

66
libevmjit/Array.cpp

@ -6,11 +6,8 @@
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
#include "Runtime.h"
#include "Utils.h" #include "Utils.h"
#include <set> // DEBUG only
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -19,7 +16,6 @@ namespace jit
{ {
static const auto c_reallocStep = 1; static const auto c_reallocStep = 1;
static const auto c_reallocMultipier = 2;
llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name) llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name)
{ {
@ -47,9 +43,9 @@ llvm::Function* Array::createArrayPushFunc()
auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func); auto pushBB = llvm::BasicBlock::Create(m_builder.getContext(), "Push", func);
m_builder.SetInsertPoint(entryBB); m_builder.SetInsertPoint(entryBB);
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto sizePtr = m_builder.CreateStructGEP(arrayPtr, 1, "sizePtr"); auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr");
auto capPtr = m_builder.CreateStructGEP(arrayPtr, 2, "capPtr"); auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr");
auto data = m_builder.CreateLoad(dataPtr, "data"); auto data = m_builder.CreateLoad(dataPtr, "data");
auto size = m_builder.CreateLoad(sizePtr, "size"); auto size = m_builder.CreateLoad(sizePtr, "size");
auto cap = m_builder.CreateLoad(capPtr, "cap"); auto cap = m_builder.CreateLoad(capPtr, "cap");
@ -58,7 +54,6 @@ llvm::Function* Array::createArrayPushFunc()
m_builder.SetInsertPoint(reallocBB); m_builder.SetInsertPoint(reallocBB);
auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap"); 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 reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32
auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes"); auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes");
auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes"); auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes");
@ -96,7 +91,7 @@ llvm::Function* Array::createArraySetFunc()
InsertPointGuard guard{m_builder}; InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto data = m_builder.CreateLoad(dataPtr, "data"); auto data = m_builder.CreateLoad(dataPtr, "data");
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr");
m_builder.CreateStore(value, valuePtr); m_builder.CreateStore(value, valuePtr);
@ -118,7 +113,7 @@ llvm::Function* Array::createArrayGetFunc()
InsertPointGuard guard{m_builder}; InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto data = m_builder.CreateLoad(dataPtr, "data"); auto data = m_builder.CreateLoad(dataPtr, "data");
auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr"); auto valuePtr = m_builder.CreateGEP(data, index, "valuePtr");
auto value = m_builder.CreateLoad(valuePtr, "value"); auto value = m_builder.CreateLoad(valuePtr, "value");
@ -163,7 +158,7 @@ llvm::Function* Array::createFreeFunc()
InsertPointGuard guard{m_builder}; InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateStructGEP(arrayPtr, 0, "dataPtr"); auto dataPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 0, "dataPtr");
auto data = m_builder.CreateLoad(dataPtr, "data"); auto data = m_builder.CreateLoad(dataPtr, "data");
auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem"); auto mem = m_builder.CreateBitCast(data, Type::BytePtr, "mem");
m_builder.CreateCall(freeFunc, mem); m_builder.CreateCall(freeFunc, mem);
@ -199,8 +194,8 @@ llvm::Function* Array::createExtendFunc()
InsertPointGuard guard{m_builder}; InsertPointGuard guard{m_builder};
m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func)); m_builder.SetInsertPoint(llvm::BasicBlock::Create(m_builder.getContext(), {}, func));
auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");// TODO: Use byte* in Array auto dataPtr = m_builder.CreateBitCast(arrayPtr, Type::BytePtr->getPointerTo(), "dataPtr");// TODO: Use byte* in Array
auto sizePtr = m_builder.CreateStructGEP(arrayPtr, 1, "sizePtr"); auto sizePtr = m_builder.CreateStructGEP(getType(), arrayPtr, 1, "sizePtr");
auto capPtr = m_builder.CreateStructGEP(arrayPtr, 2, "capPtr"); auto capPtr = m_builder.CreateStructGEP(getType(), arrayPtr, 2, "capPtr");
auto data = m_builder.CreateLoad(dataPtr, "data"); auto data = m_builder.CreateLoad(dataPtr, "data");
auto size = m_builder.CreateLoad(sizePtr, "size"); auto size = m_builder.CreateLoad(sizePtr, "size");
auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize"); auto extSize = m_builder.CreateNUWSub(newSize, size, "extSize");
@ -246,7 +241,7 @@ Array::Array(llvm::IRBuilder<>& _builder, llvm::Value* _array) :
void Array::pop(llvm::Value* _count) void Array::pop(llvm::Value* _count)
{ {
auto sizePtr = m_builder.CreateStructGEP(m_array, 1, "sizePtr"); auto sizePtr = m_builder.CreateStructGEP(getType(), m_array, 1, "sizePtr");
auto size = m_builder.CreateLoad(sizePtr, "size"); auto size = m_builder.CreateLoad(sizePtr, "size");
auto newSize = m_builder.CreateNUWSub(size, _count, "newSize"); auto newSize = m_builder.CreateNUWSub(size, _count, "newSize");
m_builder.CreateStore(newSize, sizePtr); m_builder.CreateStore(newSize, sizePtr);
@ -254,7 +249,7 @@ void Array::pop(llvm::Value* _count)
llvm::Value* Array::size(llvm::Value* _array) llvm::Value* Array::size(llvm::Value* _array)
{ {
auto sizePtr = m_builder.CreateStructGEP(_array ? _array : m_array, 1, "sizePtr"); auto sizePtr = m_builder.CreateStructGEP(getType(), _array ? _array : m_array, 1, "sizePtr");
return m_builder.CreateLoad(sizePtr, "array.size"); return m_builder.CreateLoad(sizePtr, "array.size");
} }
@ -269,52 +264,15 @@ void Array::extend(llvm::Value* _arrayPtr, llvm::Value* _size)
} }
} }
namespace
{
struct AllocatedMemoryWatchdog
{
std::set<void*> allocatedMemory;
~AllocatedMemoryWatchdog()
{
if (!allocatedMemory.empty())
{
DLOG(mem) << allocatedMemory.size() << " MEM LEAKS!\n";
for (auto&& leak : allocatedMemory)
DLOG(mem) << "\t" << leak << "\n";
}
}
};
AllocatedMemoryWatchdog watchdog;
}
extern "C" extern "C"
{ {
using namespace dev::eth::jit;
EXPORT void* ext_realloc(void* _data, size_t _size) noexcept EXPORT void* ext_realloc(void* _data, size_t _size) noexcept
{ {
//std::cerr << "REALLOC: " << _data << " [" << _size << "]" << std::endl; return std::realloc(_data, _size);
auto newData = std::realloc(_data, _size);
if (_data != newData)
{
DLOG(mem) << "REALLOC: " << newData << " <- " << _data << " [" << _size << "]\n";
watchdog.allocatedMemory.erase(_data);
watchdog.allocatedMemory.insert(newData);
}
return newData;
} }
EXPORT void ext_free(void* _data) noexcept EXPORT void ext_free(void* _data) noexcept
{ {
std::free(_data); std::free(_data);
if (_data)
{
DLOG(mem) << "FREE : " << _data << "\n";
watchdog.allocatedMemory.erase(_data);
}
} }
}
} // extern "C"

2
libevmjit/BasicBlock.h

@ -11,7 +11,7 @@ namespace eth
{ {
namespace jit namespace jit
{ {
using namespace evmjit;
using instr_idx = uint64_t; using instr_idx = uint64_t;
class BasicBlock class BasicBlock

7
libevmjit/CMakeLists.txt

@ -1,24 +1,22 @@
set(TARGET_NAME evmjit) set(TARGET_NAME evmjit)
set(SOURCES set(SOURCES
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h
Arith256.cpp Arith256.h Arith256.cpp Arith256.h
Array.cpp Array.h Array.cpp Array.h
BasicBlock.cpp BasicBlock.h BasicBlock.cpp BasicBlock.h
Cache.cpp Cache.h Cache.cpp Cache.h
Common.h Common.h
Compiler.cpp Compiler.h Compiler.cpp Compiler.h
CompilerHelper.cpp CompilerHelper.h CompilerHelper.cpp CompilerHelper.h
Endianness.cpp Endianness.h Endianness.cpp Endianness.h
ExecStats.cpp ExecStats.h ExecStats.cpp ExecStats.h
ExecutionEngine.cpp ExecutionEngine.h
Ext.cpp Ext.h Ext.cpp Ext.h
GasMeter.cpp GasMeter.h GasMeter.cpp GasMeter.h
Instruction.cpp Instruction.h Instruction.cpp Instruction.h
interface.cpp interface.h interface.cpp interface.h
Memory.cpp Memory.h Memory.cpp Memory.h
Optimizer.cpp Optimizer.h Optimizer.cpp Optimizer.h
Runtime.cpp Runtime.h
RuntimeData.h
RuntimeManager.cpp RuntimeManager.h RuntimeManager.cpp RuntimeManager.h
Stack.cpp Stack.h Stack.cpp Stack.h
Type.cpp Type.h Type.cpp Type.h
@ -79,6 +77,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES
VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION} VERSION ${EVMJIT_VERSION} SOVERSION ${EVMJIT_SOVERSION}
FOLDER "libs") FOLDER "libs")
include_directories(${EVMJIT_INCLUDE_DIR})
include_directories(${LLVM_INCLUDE_DIRS}) include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen) include_directories(${CMAKE_CURRENT_BINARY_DIR}/gen)

69
libevmjit/Cache.cpp

@ -1,5 +1,7 @@
#include "Cache.h" #include "Cache.h"
#include <mutex>
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h> #include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h> #include <llvm/IR/LLVMContext.h>
@ -10,22 +12,22 @@
#include <llvm/Support/raw_os_ostream.h> #include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "ExecutionEngine.h" #include "ExecStats.h"
#include "Utils.h" #include "Utils.h"
#include "BuildInfo.gen.h" #include "BuildInfo.gen.h"
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
namespace namespace
{ {
using Guard = std::lock_guard<std::mutex>;
std::mutex x_cacheMutex;
CacheMode g_mode; CacheMode g_mode;
llvm::MemoryBuffer* g_lastObject; std::unique_ptr<llvm::MemoryBuffer> g_lastObject;
ExecutionEngineListener* g_listener; JITListener* g_listener;
static const size_t c_versionStampLength = 32; static const size_t c_versionStampLength = 32;
llvm::StringRef getLibVersionStamp() llvm::StringRef getLibVersionStamp()
@ -40,16 +42,31 @@ 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_mode = _mode;
g_listener = _listener; 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() void Cache::clear()
{ {
Guard g{x_cacheMutex};
using namespace llvm::sys; using namespace llvm::sys;
llvm::SmallString<256> cachePath; llvm::SmallString<256> cachePath;
path::system_temp_directory(false, cachePath); path::system_temp_directory(false, cachePath);
@ -62,6 +79,8 @@ void Cache::clear()
void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache) void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string, uint64_t>& _funcCache)
{ {
Guard g{x_cacheMutex};
// TODO: Cache dir should be in one place // TODO: Cache dir should be in one place
using namespace llvm::sys; using namespace llvm::sys;
llvm::SmallString<256> cachePath; llvm::SmallString<256> cachePath;
@ -79,8 +98,7 @@ void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string,
if (auto module = getObject(name)) if (auto module = getObject(name))
{ {
DLOG(cache) << "Preload: " << name << "\n"; DLOG(cache) << "Preload: " << name << "\n";
_ee.addModule(module.get()); _ee.addModule(std::move(module));
module.release();
auto addr = _ee.getFunctionAddress(name); auto addr = _ee.getFunctionAddress(name);
assert(addr); assert(addr);
_funcCache[std::move(name)] = addr; _funcCache[std::move(name)] = addr;
@ -92,11 +110,14 @@ void Cache::preload(llvm::ExecutionEngine& _ee, std::unordered_map<std::string,
std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id) std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
{ {
Guard g{x_cacheMutex};
if (g_mode != CacheMode::on && g_mode != CacheMode::read) if (g_mode != CacheMode::on && g_mode != CacheMode::read)
return nullptr; return nullptr;
if (g_listener) // TODO: Disabled because is not thread-safe.
g_listener->stateChanged(ExecState::CacheLoad); //if (g_listener)
// g_listener->stateChanged(ExecState::CacheLoad);
DLOG(cache) << id << ": search\n"; DLOG(cache) << id << ": search\n";
if (!CHECK(!g_lastObject)) if (!CHECK(!g_lastObject))
@ -134,14 +155,17 @@ std::unique_ptr<llvm::Module> Cache::getObject(std::string const& id)
} }
void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object)
{ {
Guard g{x_cacheMutex};
// Only in "on" and "write" mode // Only in "on" and "write" mode
if (g_mode != CacheMode::on && g_mode != CacheMode::write) if (g_mode != CacheMode::on && g_mode != CacheMode::write)
return; return;
if (g_listener) // TODO: Disabled because is not thread-safe.
g_listener->stateChanged(ExecState::CacheWrite); // if (g_listener)
// g_listener->stateChanged(ExecState::CacheWrite);
auto&& id = _module->getModuleIdentifier(); auto&& id = _module->getModuleIdentifier();
llvm::SmallString<256> cachePath; llvm::SmallString<256> cachePath;
@ -154,19 +178,18 @@ void ObjectCache::notifyObjectCompiled(llvm::Module const* _module, llvm::Memory
llvm::sys::path::append(cachePath, id); llvm::sys::path::append(cachePath, id);
DLOG(cache) << id << ": write\n"; DLOG(cache) << id << ": write\n";
std::string error; std::error_code error;
llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None); llvm::raw_fd_ostream cacheFile(cachePath.c_str(), error, llvm::sys::fs::F_None);
cacheFile << _object->getBuffer() << getLibVersionStamp(); cacheFile << _object.getBuffer() << getLibVersionStamp();
} }
llvm::MemoryBuffer* ObjectCache::getObject(llvm::Module const* _module) std::unique_ptr<llvm::MemoryBuffer> ObjectCache::getObject(llvm::Module const* _module)
{ {
Guard g{x_cacheMutex};
DLOG(cache) << _module->getModuleIdentifier() << ": use\n"; DLOG(cache) << _module->getModuleIdentifier() << ": use\n";
auto o = g_lastObject; return std::move(g_lastObject);
g_lastObject = nullptr;
return o;
} }
} }
} }
}

15
libevmjit/Cache.h

@ -3,7 +3,9 @@
#include <memory> #include <memory>
#include <unordered_map> #include <unordered_map>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/ExecutionEngine/ObjectCache.h> #include <llvm/ExecutionEngine/ObjectCache.h>
#include "preprocessor/llvm_includes_end.h"
namespace llvm namespace llvm
{ {
@ -12,11 +14,9 @@ namespace llvm
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{ {
namespace jit class JITListener;
{
class ExecutionEngineListener;
enum class CacheMode enum class CacheMode
{ {
@ -32,20 +32,20 @@ class ObjectCache : public llvm::ObjectCache
{ {
public: public:
/// notifyObjectCompiled - Provides a pointer to compiled code for Module M. /// notifyObjectCompiled - Provides a pointer to compiled code for Module M.
virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBuffer const* _object) final override; virtual void notifyObjectCompiled(llvm::Module const* _module, llvm::MemoryBufferRef _object) final override;
/// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that /// getObjectCopy - Returns a pointer to a newly allocated MemoryBuffer that
/// contains the object which corresponds with Module M, or 0 if an object is /// contains the object which corresponds with Module M, or 0 if an object is
/// not available. The caller owns both the MemoryBuffer returned by this /// not available. The caller owns both the MemoryBuffer returned by this
/// and the memory it references. /// and the memory it references.
virtual llvm::MemoryBuffer* getObject(llvm::Module const* _module) final override; virtual std::unique_ptr<llvm::MemoryBuffer> getObject(llvm::Module const* _module) final override;
}; };
class Cache class Cache
{ {
public: public:
static ObjectCache* getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener); static ObjectCache* init(CacheMode _mode, JITListener* _listener);
static std::unique_ptr<llvm::Module> getObject(std::string const& id); static std::unique_ptr<llvm::Module> getObject(std::string const& id);
/// Clears cache storage /// Clears cache storage
@ -57,4 +57,3 @@ public:
} }
} }
}

49
libevmjit/Common.h

@ -1,63 +1,16 @@
#pragma once #pragma once
#include <tuple>
#include <cstdint> #include <cstdint>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
using byte = uint8_t; using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
using code_iterator = byte const*; 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,
};
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t a = 0;
uint64_t b = 0;
uint64_t c = 0;
uint64_t d = 0;
};
static_assert(sizeof(i256) == 32, "Wrong i265 size");
#define UNTESTED assert(false) #define UNTESTED assert(false)
} }
} }
}

162
libevmjit/Compiler.cpp

@ -49,6 +49,11 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn
return _curr + offset; return _curr + offset;
}; };
// Skip all STOPs in the end
for (; _codeEnd != _codeBegin; --_codeEnd)
if (*(_codeEnd - 1) != static_cast<byte>(Instruction::STOP))
break;
auto begin = _codeBegin; // begin of current block auto begin = _codeBegin; // begin of current block
bool nextJumpDest = false; bool nextJumpDest = false;
for (auto curr = begin, next = begin; curr != _codeEnd; curr = next) for (auto curr = begin, next = begin; curr != _codeEnd; curr = next)
@ -148,7 +153,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp"); auto fp = m_builder.CreateCall(frameaddress, m_builder.getInt32(0), "fp");
m_builder.CreateStore(fp, jmpBufWords); m_builder.CreateStore(fp, jmpBufWords);
auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave); auto stacksave = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::stacksave);
auto sp = m_builder.CreateCall(stacksave, "sp"); auto sp = m_builder.CreateCall(stacksave, {}, "sp");
auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp"); auto jmpBufSp = m_builder.CreateConstInBoundsGEP1_64(jmpBufWords, 2, "jmpBuf.sp");
m_builder.CreateStore(sp, jmpBufSp); m_builder.CreateStore(sp, jmpBufSp);
auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp); auto setjmp = llvm::Intrinsic::getDeclaration(module.get(), llvm::Intrinsic::eh_sjlj_setjmp);
@ -159,10 +164,10 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
// TODO: Create Stop basic block on demand // TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc); 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(); 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) for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt)
{ {
@ -178,7 +183,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
m_builder.SetInsertPoint(m_stopBB); m_builder.SetInsertPoint(m_stopBB);
runtimeManager.exit(ReturnCode::Stop); runtimeManager.exit(ReturnCode::Stop);
m_builder.SetInsertPoint(abortBB); m_builder.SetInsertPoint(m_abortBB);
runtimeManager.exit(ReturnCode::OutOfGas); runtimeManager.exit(ReturnCode::OutOfGas);
removeDeadBlocks(); removeDeadBlocks();
@ -270,44 +275,96 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
{ {
auto lhs = stack.pop(); auto lhs = stack.pop();
auto rhs = stack.pop(); auto rhs = stack.pop();
auto res = _arith.mul(lhs, rhs); auto res = m_builder.CreateMul(lhs, rhs);
stack.push(res); stack.push(res);
break; break;
} }
case Instruction::DIV: case Instruction::DIV:
{ {
auto lhs = stack.pop(); auto d = stack.pop();
auto rhs = stack.pop(); auto n = stack.pop();
auto res = _arith.div(lhs, rhs); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
stack.push(res.first); 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; break;
} }
case Instruction::SDIV: case Instruction::SDIV:
{ {
auto lhs = stack.pop(); auto d = stack.pop();
auto rhs = stack.pop(); auto n = stack.pop();
auto res = _arith.sdiv(lhs, rhs); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
stack.push(res.first); 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; break;
} }
case Instruction::MOD: case Instruction::MOD:
{ {
auto lhs = stack.pop(); auto d = stack.pop();
auto rhs = stack.pop(); auto n = stack.pop();
auto res = _arith.div(lhs, rhs); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
stack.push(res.second); 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; break;
} }
case Instruction::SMOD: case Instruction::SMOD:
{ {
auto lhs = stack.pop(); auto d = stack.pop();
auto rhs = stack.pop(); auto n = stack.pop();
auto res = _arith.sdiv(lhs, rhs); auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
stack.push(res.second); 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; break;
} }
@ -417,59 +474,36 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::BYTE: case Instruction::BYTE:
{ {
const auto byteNum = stack.pop(); const auto idx = stack.pop();
auto value = 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 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 // TODO: Workaround for LLVM bug. Using big value of index causes invalid memory access.
auto byte = m_builder.CreateExtractElement(bytes, safeByteNum, "byte"); auto safeIdx = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5));
// TODO: Workaround for LLVM bug. DAG Builder used sext on index instead of zext
safeIdx = m_builder.CreateZExt(safeIdx, Type::Size);
auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte");
value = m_builder.CreateZExt(byte, Type::Word); value = m_builder.CreateZExt(byte, Type::Word);
value = m_builder.CreateSelect(idxValid, value, Constant::get(0));
auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32));
value = m_builder.CreateSelect(byteNumValid, value, Constant::get(0));
stack.push(value); stack.push(value);
break; 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: case Instruction::SIGNEXTEND:
{ {
auto idx = stack.pop(); auto idx = stack.pop();
auto word = stack.pop(); auto word = stack.pop();
auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32"); 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"); auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8");
// test for word >> (k * 8 + 7) // test for word >> (k * 8 + 7)
auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos"); auto bitpos = m_builder.CreateAdd(k32x8, m_builder.getInt64(7), "bitpos");
auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word); auto bitposEx = m_builder.CreateZExt(bitpos, Type::Word);
auto bittester = m_builder.CreateShl(Constant::get(1), bitposEx); auto bitval = m_builder.CreateLShr(word, bitposEx, "bitval");
auto bitresult = m_builder.CreateAnd(word, bittester); auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest");
auto bittest = m_builder.CreateICmpUGT(bitresult, Constant::get(0));
// FIXME: The following does not work - LLVM bug, report!
//auto bitval = m_builder.CreateLShr(word, bitpos, "bitval");
//auto bittest = m_builder.CreateTrunc(bitval, Type::Bool, "bittest");
auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx); auto mask_ = m_builder.CreateShl(Constant::get(1), bitposEx);
auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask"); auto mask = m_builder.CreateSub(mask_, Constant::get(1), "mask");
@ -499,11 +533,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::POP: case Instruction::POP:
{ {
auto val = stack.pop(); stack.pop();
static_cast<void>(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");
break; break;
} }
@ -660,7 +690,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
} }
case Instruction::CODESIZE: case Instruction::CODESIZE:
// TODO: Use constant
stack.push(_runtimeManager.getCodeSize()); stack.push(_runtimeManager.getCodeSize());
break; break;
@ -733,8 +762,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::CALLDATALOAD: case Instruction::CALLDATALOAD:
{ {
auto index = stack.pop(); auto idx = stack.pop();
auto value = _ext.calldataload(index); auto value = _ext.calldataload(idx);
stack.push(value); stack.push(value);
break; break;
} }
@ -801,7 +830,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::STOP: case Instruction::STOP:
{ {
m_builder.CreateRet(Constant::get(ReturnCode::Stop)); m_builder.CreateBr(m_stopBB);
break; break;
} }
@ -828,7 +857,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
} }
default: // Invalid instruction - abort default: // Invalid instruction - abort
m_builder.CreateRet(Constant::get(ReturnCode::BadInstruction)); m_builder.CreateBr(m_abortBB);
it = _basicBlock.end() - 1; // finish block compilation it = _basicBlock.end() - 1; // finish block compilation
} }
} }
@ -946,4 +975,3 @@ void Compiler::dump()
} }
} }
} }

4
libevmjit/Compiler.h

@ -1,6 +1,5 @@
#pragma once #pragma once
#include "Common.h"
#include "BasicBlock.h" #include "BasicBlock.h"
namespace dev namespace dev
@ -65,6 +64,9 @@ private:
/// Stop basic block - terminates execution with STOP code (0) /// Stop basic block - terminates execution with STOP code (0)
llvm::BasicBlock* m_stopBB = nullptr; llvm::BasicBlock* m_stopBB = nullptr;
/// Abort basic block - terminates execution with OOG-like state
llvm::BasicBlock* m_abortBB = nullptr;
/// Block with a jump table. /// Block with a jump table.
std::unique_ptr<BasicBlock> m_jumpTableBlock; std::unique_ptr<BasicBlock> m_jumpTableBlock;

25
libevmjit/CompilerHelper.h

@ -37,7 +37,6 @@ protected:
friend class RuntimeHelper; friend class RuntimeHelper;
}; };
/// Compiler helper that depends on runtime data /// Compiler helper that depends on runtime data
class RuntimeHelper : public CompilerHelper class RuntimeHelper : public CompilerHelper
{ {
@ -50,29 +49,7 @@ private:
RuntimeManager& m_runtimeManager; RuntimeManager& m_runtimeManager;
}; };
using InsertPointGuard = llvm::IRBuilderBase::InsertPointGuard;
/// 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;
};
} }
} }

5
libevmjit/Endianness.cpp

@ -18,9 +18,8 @@ llvm::Value* Endianness::bswapIfLE(llvm::IRBuilder<>& _builder, llvm::Value* _wo
{ {
if (llvm::sys::IsLittleEndianHost) if (llvm::sys::IsLittleEndianHost)
{ {
// FIXME: Disabled because of problems with BYTE if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_word))
//if (auto constant = llvm::dyn_cast<llvm::ConstantInt>(_word)) return _builder.getInt(constant->getValue().byteSwap());
// return _builder.getInt(constant->getValue().byteSwap());
// OPT: Cache func declaration? // OPT: Cache func declaration?
auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::Word); auto bswapFunc = llvm::Intrinsic::getDeclaration(_builder.GetInsertBlock()->getParent()->getParent(), llvm::Intrinsic::bswap, Type::Word);

5
libevmjit/ExecStats.cpp

@ -8,9 +8,7 @@
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
void ExecStats::stateChanged(ExecState _state) void ExecStats::stateChanged(ExecState _state)
@ -95,4 +93,3 @@ StatsCollector::~StatsCollector()
} }
} }
}

35
libevmjit/ExecStats.h

@ -1,19 +1,43 @@
#pragma once #pragma once
#include <memory>
#include <vector> #include <vector>
#include <string> #include <string>
#include <chrono> #include <chrono>
#include "ExecutionEngine.h"
namespace dev 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: public:
using clock = std::chrono::high_resolution_clock; using clock = std::chrono::high_resolution_clock;
@ -42,4 +66,3 @@ public:
} }
} }
}

210
libevmjit/ExecutionEngine.cpp

@ -1,210 +0,0 @@
#include "ExecutionEngine.h"
#include <array>
#include <mutex>
#include <iostream>
#include <unordered_map>
#include <cstdlib>
#include <cstring>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/ADT/Triple.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/SectionMemoryManager.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/ManagedStatic.h>
#include "preprocessor/llvm_includes_end.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
{
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<byte, size>*)&_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<bool> g_optimize{"O", cl::desc{"Optimize"}};
cl::opt<CacheMode> 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<bool> g_stats{"st", cl::desc{"Statistics"}};
cl::opt<bool> 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");
// FIXME: LLVM workaround:
// Manually select instruction scheduler. Confirmed bad schedulers: source, list-burr, list-hybrid.
// "source" scheduler has a bug: http://llvm.org/bugs/show_bug.cgi?id=22304
auto envLine = std::getenv("EVMJIT");
auto commandLine = std::string{"evmjit "} + (envLine ? envLine : "") + " -pre-RA-sched=list-ilp\0";
static const auto c_maxArgs = 20;
char const* argv[c_maxArgs] = {nullptr, };
auto arg = std::strtok(&*commandLine.begin(), " ");
auto i = 0;
for (; i < c_maxArgs && arg; ++i, arg = std::strtok(nullptr, " "))
argv[i] = arg;
cl::ParseCommandLineOptions(i, argv, "Ethereum EVM JIT Compiler");
}
}
ReturnCode ExecutionEngine::run(RuntimeData* _data, Env* _env)
{
static std::once_flag flag;
std::call_once(flag, parseOptions);
std::unique_ptr<ExecStats> 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::unordered_map<std::string, uint64_t> funcCache;
static std::unique_ptr<llvm::ExecutionEngine> ee;
if (!ee)
{
if (g_cache == CacheMode::clear)
Cache::clear();
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
auto module = std::unique_ptr<llvm::Module>(new llvm::Module({}, llvm::getGlobalContext()));
llvm::EngineBuilder builder(module.get());
builder.setEngineKind(llvm::EngineKind::JIT);
builder.setUseMCJIT(true);
builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None);
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());
ee.reset(builder.create());
if (!CHECK(ee))
return ReturnCode::LLVMConfigError;
module.release(); // Successfully created llvm::ExecutionEngine takes ownership of the module
ee->setObjectCache(objectCache);
if (preloadCache)
Cache::preload(*ee, funcCache);
}
static StatsCollector statsCollector;
auto mainFuncName = codeHash(_data->codeHash);
m_runtime.init(_data, _env);
EntryFuncPtr entryFuncPtr = nullptr;
auto it = funcCache.find(mainFuncName);
if (it != funcCache.end())
entryFuncPtr = (EntryFuncPtr) it->second;
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(module.get());
module.release();
listener->stateChanged(ExecState::CodeGen);
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
}
if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError;
if (it == funcCache.end())
funcCache[mainFuncName] = (uint64_t) entryFuncPtr;
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;
}
}
}
}

59
libevmjit/ExecutionEngine.h

@ -1,59 +0,0 @@
#pragma once
#include <memory>
#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;
};
}
}
}

24
libevmjit/Ext.cpp

@ -45,7 +45,6 @@ std::array<FuncDesc, sizeOf<EnvFunc>::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_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_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})}, 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; 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 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(); auto ret = getArgAlloca();
createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret}); auto result = m_builder.CreateBitCast(ret, Type::BytePtr);
ret = m_builder.CreateLoad(ret);
return Endianness::toNative(m_builder, ret); 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) llvm::Value* Ext::balance(llvm::Value* _address)

2
libevmjit/Ext.h

@ -35,7 +35,6 @@ enum class EnvFunc
log, log,
blockhash, blockhash,
extcode, extcode,
calldataload, // Helper function, not client Env interface
_size _size
}; };
@ -63,7 +62,6 @@ private:
Memory& m_memoryMan; Memory& m_memoryMan;
llvm::Value* m_size; llvm::Value* m_size;
llvm::Value* m_data = nullptr;
std::array<llvm::Function*, sizeOf<EnvFunc>::value> m_funcs; std::array<llvm::Function*, sizeOf<EnvFunc>::value> m_funcs;
std::array<llvm::Value*, 8> m_argAllocas; std::array<llvm::Value*, 8> m_argAllocas;

22
libevmjit/GasMeter.cpp

@ -216,22 +216,12 @@ void GasMeter::countExp(llvm::Value* _exponent)
// cost = ((256 - lz) + 7) / 8 // cost = ((256 - lz) + 7) / 8
// OPT: Can gas update be done in exp algorithm? // OPT: Can gas update be done in exp algorithm?
auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word);
auto t = llvm::APInt{256, 1}; auto lz256 = m_builder.CreateCall(ctlz, {_exponent, m_builder.getInt1(false)});
auto c = m_builder.CreateSelect(m_builder.CreateICmpUGE(_exponent, Constant::get(t)), m_builder.getInt64(1), m_builder.getInt64(0)); auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz");
for (auto i = 2; i <= 32; ++i) auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits");
{ auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8));
t <<= 8; count(m_builder.CreateNUWMul(sigBytes, m_builder.getInt64(c_expByteGas)));
c = m_builder.CreateSelect(m_builder.CreateICmpUGE(_exponent, Constant::get(t)), m_builder.getInt64(i), c);
}
// FIXME: Does not work because of LLVM bug: https://llvm.org/bugs/show_bug.cgi?id=22304
// auto ctlz = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, Type::Word);
// auto lz256 = m_builder.CreateCall2(ctlz, _exponent, m_builder.getInt1(false));
// auto lz = m_builder.CreateTrunc(lz256, Type::Gas, "lz");
// auto sigBits = m_builder.CreateSub(m_builder.getInt64(256), lz, "sigBits");
// auto sigBytes = m_builder.CreateUDiv(m_builder.CreateAdd(sigBits, m_builder.getInt64(7)), m_builder.getInt64(8));
count(m_builder.CreateNUWMul(c, m_builder.getInt64(c_expByteGas)));
} }
void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue) void GasMeter::countSStore(Ext& _ext, llvm::Value* _index, llvm::Value* _newValue)

1
libevmjit/GasMeter.h

@ -10,6 +10,7 @@ namespace eth
namespace jit namespace jit
{ {
class RuntimeManager; class RuntimeManager;
using namespace evmjit;
class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper class GasMeter : public CompilerHelper // TODO: Use RuntimeHelper
{ {

5
libevmjit/Instruction.cpp

@ -6,9 +6,7 @@
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
llvm::APInt readPushData(code_iterator& _curr, code_iterator _end) llvm::APInt readPushData(code_iterator& _curr, code_iterator _end)
@ -39,4 +37,3 @@ void skipPushData(code_iterator& _curr, code_iterator _end)
} }
} }
}

5
libevmjit/Instruction.h

@ -9,9 +9,7 @@ namespace llvm
namespace dev namespace dev
{ {
namespace eth namespace evmjit
{
namespace jit
{ {
/// Virtual machine bytecode instruction. /// Virtual machine bytecode instruction.
@ -236,4 +234,3 @@ void skipPushData(code_iterator& _curr, code_iterator _end);
} }
} }
}

252
libevmjit/JIT.cpp

@ -0,0 +1,252 @@
#include "evmjit/JIT.h"
#include <array>
#include <mutex>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/ADT/Triple.h>
#include <llvm/ExecutionEngine/MCJIT.h>
#include <llvm/Support/TargetSelect.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/CommandLine.h>
#include <llvm/Support/ManagedStatic.h>
#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*);
std::string hash2str(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<byte, size>*)&_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<bool> g_optimize{"O", cl::desc{"Optimize"}};
cl::opt<CacheMode> 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<bool> g_stats{"st", cl::desc{"Statistics"}};
cl::opt<bool> 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<llvm::ExecutionEngine> m_engine;
mutable std::mutex x_codeMap;
std::unordered_map<h256, ExecFunc> 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);
ExecFunc compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash);
};
JITImpl::JITImpl()
{
parseOptions();
bool preloadCache = g_cache == CacheMode::preload;
if (preloadCache)
g_cache = CacheMode::on;
llvm::InitializeNativeTarget();
llvm::InitializeNativeTargetAsmPrinter();
auto module = std::unique_ptr<llvm::Module>(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
{
std::lock_guard<std::mutex> lock{x_codeMap};
auto it = m_codeMap.find(_codeHash);
if (it != m_codeMap.end())
return it->second;
return nullptr;
}
void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _funcAddr)
{
std::lock_guard<std::mutex> lock{x_codeMap};
m_codeMap.emplace(std::move(_codeHash), _funcAddr);
}
ExecFunc JITImpl::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash)
{
auto name = hash2str(_codeHash);
auto module = Cache::getObject(name);
if (!module)
{
// TODO: Listener support must be redesigned. These should be a feature of JITImpl
//listener->stateChanged(ExecState::Compilation);
assert(_code || !_codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler{{}}.compile(_code, _code + _codeSize, name);
if (g_optimize)
{
//listener->stateChanged(ExecState::Optimization);
optimize(*module);
}
prepare(*module);
}
if (g_dump)
module->dump();
m_engine->addModule(std::move(module));
//listener->stateChanged(ExecState::CodeGen);
return (ExecFunc)m_engine->getFunctionAddress(name);
}
} // anonymous namespace
bool JIT::isCodeReady(h256 const& _codeHash)
{
return JITImpl::instance().getExecFunc(_codeHash) != nullptr;
}
void JIT::compile(byte const* _code, uint64_t _codeSize, h256 const& _codeHash)
{
auto& jit = JITImpl::instance();
auto execFunc = jit.compile(_code, _codeSize, _codeHash);
if (execFunc) // FIXME: What with error?
jit.mapExecFunc(_codeHash, execFunc);
}
ReturnCode JIT::exec(ExecutionContext& _context)
{
//std::unique_ptr<ExecStats> listener{new ExecStats};
//listener->stateChanged(ExecState::Started);
//static StatsCollector statsCollector;
auto& jit = JITImpl::instance();
auto codeHash = _context.codeHash();
auto execFunc = jit.getExecFunc(codeHash);
if (!execFunc)
{
execFunc = jit.compile(_context.code(), _context.codeSize(), codeHash);
if (!execFunc)
return ReturnCode::LLVMError;
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;
}
extern "C" void ext_free(void* _data) noexcept;
ExecutionContext::~ExecutionContext()
{
if (m_memData)
ext_free(m_memData); // Use helper free to check memory leaks
}
bytes_ref ExecutionContext::getReturnData() const
{
auto data = m_data->callData;
auto size = static_cast<size_t>(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};
}
}
}

6
libevmjit/Memory.cpp

@ -5,7 +5,6 @@
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "Type.h" #include "Type.h"
#include "Runtime.h"
#include "GasMeter.h" #include "GasMeter.h"
#include "Endianness.h" #include "Endianness.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
@ -191,8 +190,7 @@ llvm::Value* Memory::getSize()
llvm::Value* Memory::getBytePtr(llvm::Value* _index) 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(), _index, "ptr");
return m_builder.CreateGEP(getData(), idx, "ptr");
} }
void Memory::require(llvm::Value* _offset, llvm::Value* _size) 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 bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero");
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src"); 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 padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx");
auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx); auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx);
auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx); auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx);

103
libevmjit/Optimizer.cpp

@ -1,11 +1,16 @@
#include "Optimizer.h" #include "Optimizer.h"
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/PassManager.h> #include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Transforms/Scalar.h> #include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/IPO.h> #include <llvm/Transforms/IPO.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "Arith256.h"
#include "Type.h"
namespace dev namespace dev
{ {
namespace eth namespace eth
@ -15,15 +20,105 @@ namespace jit
bool optimize(llvm::Module& _module) bool optimize(llvm::Module& _module)
{ {
auto pm = llvm::PassManager{}; auto pm = llvm::legacy::PassManager{};
//pm.add(llvm::createFunctionInliningPass(2, 2)); // Produces invalid IR pm.add(llvm::createFunctionInliningPass(2, 2));
pm.add(llvm::createCFGSimplificationPass()); pm.add(llvm::createCFGSimplificationPass());
//pm.add(llvm::createInstructionCombiningPass()); // Produces invalid runtime results pm.add(llvm::createInstructionCombiningPass());
pm.add(llvm::createAggressiveDCEPass()); pm.add(llvm::createAggressiveDCEPass());
pm.add(llvm::createLowerSwitchPass()); pm.add(llvm::createLowerSwitchPass());
return pm.run(_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);
}
} }
} }
} }

2
libevmjit/Optimizer.h

@ -14,6 +14,8 @@ namespace jit
bool optimize(llvm::Module& _module); bool optimize(llvm::Module& _module);
bool prepare(llvm::Module& _module);
} }
} }
} }

43
libevmjit/Runtime.cpp

@ -1,43 +0,0 @@
#include "Runtime.h"
#include <cassert>
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<size_t>(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};
}
}
}
}

30
libevmjit/Runtime.h

@ -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;
};
}
}
}

60
libevmjit/RuntimeData.h

@ -1,60 +0,0 @@
#pragma once
#include "Common.h"
namespace dev
{
namespace eth
{
namespace jit
{
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;
i256 codeHash;
};
/// VM Environment (ExtVM) opaque type
struct Env;
}
}
}

64
libevmjit/RuntimeManager.cpp

@ -64,22 +64,22 @@ llvm::Twine getName(RuntimeData::Index _index)
{ {
switch (_index) switch (_index)
{ {
default: return "data"; default: return "";
case RuntimeData::Address: return "address"; case RuntimeData::Gas: return "msg.gas";
case RuntimeData::Caller: return "caller"; case RuntimeData::GasPrice: return "tx.gasprice";
case RuntimeData::Origin: return "origin"; case RuntimeData::CallData: return "msg.data.ptr";
case RuntimeData::CallValue: return "callvalue"; case RuntimeData::CallDataSize: return "msg.data.size";
case RuntimeData::GasPrice: return "gasprice"; case RuntimeData::Address: return "this.address";
case RuntimeData::CoinBase: return "coinbase"; case RuntimeData::Caller: return "msg.caller";
case RuntimeData::Difficulty: return "difficulty"; case RuntimeData::Origin: return "tx.origin";
case RuntimeData::GasLimit: return "gaslimit"; case RuntimeData::CallValue: return "msg.value";
case RuntimeData::CallData: return "callData"; case RuntimeData::CoinBase: return "block.coinbase";
case RuntimeData::Code: return "code"; case RuntimeData::Difficulty: return "block.difficulty";
case RuntimeData::CodeSize: return "code"; case RuntimeData::GasLimit: return "block.gaslimit";
case RuntimeData::CallDataSize: return "callDataSize"; case RuntimeData::Number: return "block.number";
case RuntimeData::Gas: return "gas"; case RuntimeData::Timestamp: return "block.timestamp";
case RuntimeData::Number: return "number"; case RuntimeData::Code: return "code.ptr";
case RuntimeData::Timestamp: return "timestamp"; case RuntimeData::CodeSize: return "code.size";
} }
} }
} }
@ -93,18 +93,23 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
// Unpack data // Unpack data
auto rtPtr = getRuntimePtr(); auto rtPtr = getRuntimePtr();
m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr");
assert(m_dataPtr->getType() == Type::RuntimeDataPtr); assert(m_dataPtr->getType() == Type::RuntimeDataPtr);
m_gasPtr = m_builder.CreateStructGEP(m_dataPtr, 0, "gas"); m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem");
assert(m_gasPtr->getType() == Type::Gas->getPointerTo());
m_memPtr = m_builder.CreateStructGEP(rtPtr, 2, "mem");
assert(m_memPtr->getType() == Array::getType()->getPointerTo()); assert(m_memPtr->getType() == Array::getType()->getPointerTo());
m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 1), "env"); m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env");
assert(m_envPtr->getType() == Type::EnvPtr); assert(m_envPtr->getType() == Type::EnvPtr);
m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize"); m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize");
m_builder.CreateStore(m_builder.getInt64(0), m_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}; 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 = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule());
m_checkStackLimit->setDoesNotThrow(); m_checkStackLimit->setDoesNotThrow();
@ -160,7 +165,7 @@ llvm::Value* RuntimeManager::getDataPtr()
return m_dataPtr; return m_dataPtr;
auto rtPtr = getRuntimePtr(); auto rtPtr = getRuntimePtr();
auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(rtPtr, 0), "data"); auto dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data");
assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo()); assert(dataPtr->getType() == getRuntimeDataType()->getPointerTo());
return dataPtr; return dataPtr;
} }
@ -173,14 +178,14 @@ llvm::Value* RuntimeManager::getEnvPtr()
llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index) llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index)
{ {
auto ptr = getBuilder().CreateStructGEP(getDataPtr(), _index); auto ptr = getBuilder().CreateStructGEP(getRuntimeDataType(), getDataPtr(), _index);
assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType()); assert(getRuntimeDataType()->getElementType(_index)->getPointerTo() == ptr->getType());
return ptr; return ptr;
} }
llvm::Value* RuntimeManager::get(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) 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 memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo());
auto mem = getBuilder().CreateLoad(memPtr, "memory"); 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, _offset);
auto returnDataPtr = getBuilder().CreateGEP(mem, idx);
set(RuntimeData::ReturnData, returnDataPtr); set(RuntimeData::ReturnData, returnDataPtr);
auto size64 = getBuilder().CreateTrunc(_size, Type::Size); auto size64 = getBuilder().CreateTrunc(_size, Type::Size);
@ -212,6 +216,8 @@ void RuntimeManager::exit(ReturnCode _returnCode)
if (m_stack) if (m_stack)
m_stack->free(); 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)); m_builder.CreateRet(Constant::get(_returnCode));
} }
@ -265,9 +271,7 @@ llvm::Value* RuntimeManager::getCallDataSize()
llvm::Value* RuntimeManager::getGas() llvm::Value* RuntimeManager::getGas()
{ {
auto gas = get(RuntimeData::Gas); return getBuilder().CreateLoad(getGasPtr(), "gas");
assert(gas->getType() == Type::Gas);
return gas;
} }
llvm::Value* RuntimeManager::getGasPtr() llvm::Value* RuntimeManager::getGasPtr()
@ -285,7 +289,7 @@ llvm::Value* RuntimeManager::getMem()
void RuntimeManager::setGas(llvm::Value* _gas) void RuntimeManager::setGas(llvm::Value* _gas)
{ {
assert(_gas->getType() == Type::Gas); assert(_gas->getType() == Type::Gas);
set(RuntimeData::Gas, _gas); getBuilder().CreateStore(_gas, getGasPtr());
} }
} }

6
libevmjit/RuntimeManager.h

@ -1,8 +1,9 @@
#pragma once #pragma once
#include <array>
#include "CompilerHelper.h" #include "CompilerHelper.h"
#include "Type.h" #include "Type.h"
#include "RuntimeData.h"
#include "Instruction.h" #include "Instruction.h"
namespace dev namespace dev
@ -11,6 +12,7 @@ namespace eth
{ {
namespace jit namespace jit
{ {
using namespace evmjit;
class Stack; class Stack;
class RuntimeManager: public CompilerHelper class RuntimeManager: public CompilerHelper
@ -61,6 +63,8 @@ private:
llvm::Value* m_memPtr = nullptr; llvm::Value* m_memPtr = nullptr;
llvm::Value* m_envPtr = nullptr; llvm::Value* m_envPtr = nullptr;
std::array<llvm::Value*, RuntimeData::numElements> m_dataElts;
llvm::Value* m_stackSize = nullptr; llvm::Value* m_stackSize = nullptr;
llvm::Function* m_checkStackLimit = nullptr; llvm::Function* m_checkStackLimit = nullptr;

120
libevmjit/Stack.cpp

@ -5,7 +5,6 @@
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h" #include "RuntimeManager.h"
#include "Runtime.h"
#include "Utils.h" #include "Utils.h"
#include <set> // DEBUG only #include <set> // DEBUG only
@ -23,96 +22,6 @@ Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
m_stack(_builder, "stack") 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() llvm::Function* Stack::getGetFunc()
{ {
auto& func = m_get; 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<decltype(index)>::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"

8
libevmjit/Stack.h

@ -24,18 +24,10 @@ public:
void free() { m_stack.free(); } void free() { m_stack.free(); }
private: private:
llvm::Function* getPopFunc();
llvm::Function* getPushFunc();
llvm::Function* getGetFunc(); llvm::Function* getGetFunc();
llvm::Function* getSetFunc();
RuntimeManager& m_runtimeManager; RuntimeManager& m_runtimeManager;
llvm::Function* m_pop = nullptr;
llvm::Function* m_push = nullptr;
llvm::Function* m_get = nullptr; llvm::Function* m_get = nullptr;
llvm::Function* m_set = nullptr;
Array m_stack; Array m_stack;
}; };

3
libevmjit/Type.cpp

@ -13,7 +13,6 @@ namespace jit
llvm::IntegerType* Type::Word; llvm::IntegerType* Type::Word;
llvm::PointerType* Type::WordPtr; llvm::PointerType* Type::WordPtr;
llvm::IntegerType* Type::lowPrecision;
llvm::IntegerType* Type::Bool; llvm::IntegerType* Type::Bool;
llvm::IntegerType* Type::Size; llvm::IntegerType* Type::Size;
llvm::IntegerType* Type::Gas; llvm::IntegerType* Type::Gas;
@ -34,8 +33,6 @@ void Type::init(llvm::LLVMContext& _context)
{ {
Word = llvm::Type::getIntNTy(_context, 256); Word = llvm::Type::getIntNTy(_context, 256);
WordPtr = Word->getPointerTo(); WordPtr = Word->getPointerTo();
lowPrecision = llvm::Type::getInt64Ty(_context);
// TODO: Size should be architecture-dependent
Bool = llvm::Type::getInt1Ty(_context); Bool = llvm::Type::getInt1Ty(_context);
Size = llvm::Type::getInt64Ty(_context); Size = llvm::Type::getInt64Ty(_context);
Gas = Size; Gas = Size;

10
libevmjit/Type.h

@ -2,10 +2,11 @@
#include "preprocessor/llvm_includes_start.h" #include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Type.h> #include <llvm/IR/Type.h>
#include <llvm/IR/Constants.h> #include <llvm/IR/Constants.h>
#include <llvm/IR/Metadata.h>
#include "preprocessor/llvm_includes_end.h" #include "preprocessor/llvm_includes_end.h"
#include "Common.h" #include "evmjit/JIT.h" // ReturnCode
namespace dev namespace dev
{ {
@ -13,16 +14,13 @@ namespace eth
{ {
namespace jit namespace jit
{ {
using namespace evmjit;
struct Type struct Type
{ {
static llvm::IntegerType* Word; static llvm::IntegerType* Word;
static llvm::PointerType* WordPtr; 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* Bool;
static llvm::IntegerType* Size; static llvm::IntegerType* Size;
static llvm::IntegerType* Gas; static llvm::IntegerType* Gas;

25
libevmjit/interface.cpp

@ -1,29 +1,28 @@
#include "ExecutionEngine.h" #include "evmjit/JIT.h"
extern "C" extern "C"
{ {
using namespace dev::evmjit;
using namespace dev::eth::jit; EXPORT void* evmjit_create(RuntimeData* _data, Env* _env) noexcept
EXPORT void* evmjit_create() noexcept
{ {
// TODO: Make sure ExecutionEngine constructor does not throw if (!_data)
return new(std::nothrow) ExecutionEngine; 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<int>(ReturnCode::UnexpectedException);
try try
{ {
auto returnCode = _engine->run(_data, _env); auto returnCode = JIT::exec(*_context);
return static_cast<int>(returnCode); return static_cast<int>(returnCode);
} }
catch(...) catch(...)

Loading…
Cancel
Save