Browse Source

Merge pull request #2280 from chfast/pr/evmjit-update

EVM JIT update
cl-refactor
Gav Wood 9 years ago
parent
commit
7a5f801e67
  1. 4
      evmjit/CMakeLists.txt
  2. 18
      evmjit/evmcc/CMakeLists.txt
  3. 210
      evmjit/evmcc/evmcc.cpp
  4. 56
      evmjit/include/evmjit/DataTypes.h
  5. 163
      evmjit/include/evmjit/JIT.h
  6. 17
      evmjit/libevmjit-cpp/Env.cpp
  7. 41
      evmjit/libevmjit-cpp/JitVM.cpp
  8. 6
      evmjit/libevmjit-cpp/JitVM.h
  9. 29
      evmjit/libevmjit-cpp/Utils.h
  10. 674
      evmjit/libevmjit/Arith256.cpp
  11. 27
      evmjit/libevmjit/Arith256.h
  12. 3
      evmjit/libevmjit/Array.cpp
  13. 2
      evmjit/libevmjit/BasicBlock.h
  14. 8
      evmjit/libevmjit/CMakeLists.txt
  15. 27
      evmjit/libevmjit/Cache.cpp
  16. 9
      evmjit/libevmjit/Cache.h
  17. 39
      evmjit/libevmjit/Common.h
  18. 152
      evmjit/libevmjit/Compiler.cpp
  19. 4
      evmjit/libevmjit/Compiler.h
  20. 25
      evmjit/libevmjit/CompilerHelper.h
  21. 5
      evmjit/libevmjit/ExecStats.cpp
  22. 35
      evmjit/libevmjit/ExecStats.h
  23. 192
      evmjit/libevmjit/ExecutionEngine.cpp
  24. 59
      evmjit/libevmjit/ExecutionEngine.h
  25. 24
      evmjit/libevmjit/Ext.cpp
  26. 2
      evmjit/libevmjit/Ext.h
  27. 1
      evmjit/libevmjit/GasMeter.h
  28. 5
      evmjit/libevmjit/Instruction.cpp
  29. 5
      evmjit/libevmjit/Instruction.h
  30. 222
      evmjit/libevmjit/JIT.cpp
  31. 6
      evmjit/libevmjit/Memory.cpp
  32. 97
      evmjit/libevmjit/Optimizer.cpp
  33. 2
      evmjit/libevmjit/Optimizer.h
  34. 43
      evmjit/libevmjit/Runtime.cpp
  35. 30
      evmjit/libevmjit/Runtime.h
  36. 63
      evmjit/libevmjit/RuntimeData.h
  37. 56
      evmjit/libevmjit/RuntimeManager.cpp
  38. 6
      evmjit/libevmjit/RuntimeManager.h
  39. 120
      evmjit/libevmjit/Stack.cpp
  40. 8
      evmjit/libevmjit/Stack.h
  41. 3
      evmjit/libevmjit/Type.cpp
  42. 9
      evmjit/libevmjit/Type.h
  43. 25
      evmjit/libevmjit/interface.cpp
  44. 2
      libevm/SmartVM.cpp

4
evmjit/CMakeLists.txt

@ -29,7 +29,3 @@ add_subdirectory(libevmjit)
if(EVMJIT_CPP)
add_subdirectory(libevmjit-cpp)
endif()
if(EVMJIT_TOOLS)
add_subdirectory(evmcc)
endif()

18
evmjit/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
evmjit/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;
}

56
evmjit/include/evmjit/DataTypes.h

@ -1,56 +0,0 @@
#pragma once
#include <cstdint>
#include <functional>
namespace dev
{
namespace evmjit
{
struct h256
{
uint64_t words[4];
};
inline bool operator==(h256 _h1, h256 _h2)
{
return _h1.words[0] == _h2.words[0] &&
_h1.words[1] == _h2.words[1] &&
_h1.words[2] == _h2.words[2] &&
_h1.words[3] == _h2.words[3];
}
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t a = 0;
uint64_t b = 0;
uint64_t c = 0;
uint64_t d = 0;
i256() = default;
i256(h256 _h)
{
a = _h.words[0];
b = _h.words[1];
c = _h.words[2];
d = _h.words[3];
}
};
}
}
namespace std
{
template<> struct hash<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]);
};
};
}

163
evmjit/include/evmjit/JIT.h

@ -1,19 +1,149 @@
#pragma once
#include "evmjit/DataTypes.h"
#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 eth
namespace evmjit
{
namespace jit
using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
/// Representation of 256-bit hash value
struct h256
{
class ExecutionEngine;
}
uint64_t words[4];
};
inline bool operator==(h256 const& _h1, h256 const& _h2)
{
return _h1.words[0] == _h2.words[0] &&
_h1.words[1] == _h2.words[1] &&
_h1.words[2] == _h2.words[2] &&
_h1.words[3] == _h2.words[3];
}
namespace evmjit
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t words[4];
i256() = default;
i256(h256 const& _h) { std::memcpy(this, &_h, sizeof(*this)); }
};
// TODO: Merge with ExecutionContext
struct RuntimeData
{
enum Index
{
Gas,
GasPrice,
CallData,
CallDataSize,
Address,
Caller,
Origin,
CallValue,
CoinBase,
Difficulty,
GasLimit,
Number,
Timestamp,
Code,
CodeSize,
SuicideDestAddress = Address, ///< Suicide balance destination address
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
};
static size_t const numElements = CodeSize + 1;
int64_t gas = 0;
int64_t gasPrice = 0;
byte const* callData = nullptr;
uint64_t callDataSize = 0;
i256 address;
i256 caller;
i256 origin;
i256 callValue;
i256 coinBase;
i256 difficulty;
i256 gasLimit;
uint64_t number = 0;
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
h256 codeHash;
};
/// VM Environment (ExtVM) opaque type
struct Env;
enum class ReturnCode
{
// Success codes
Stop = 0,
Return = 1,
Suicide = 2,
// Standard error codes
OutOfGas = -1,
StackUnderflow = -2,
BadJumpDestination = -3,
BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
// Internal error codes
LLVMConfigError = -101,
LLVMCompileError = -102,
LLVMLinkError = -103,
UnexpectedException = -111,
LinkerWorkaround = -299,
};
class ExecutionContext
{
public:
ExecutionContext() = default;
ExecutionContext(RuntimeData& _data, Env* _env) { init(_data, _env); }
ExecutionContext(ExecutionContext const&) = delete;
ExecutionContext& operator=(ExecutionContext const&) = delete;
EXPORT ~ExecutionContext();
void init(RuntimeData& _data, Env* _env) { m_data = &_data; m_env = _env; }
byte const* code() const { return m_data->code; }
uint64_t codeSize() const { return m_data->codeSize; }
h256 const& codeHash() const { return m_data->codeHash; }
bytes_ref getReturnData() const;
protected:
RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract.
Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract.
byte* m_memData = nullptr;
uint64_t m_memSize = 0;
uint64_t m_memCap = 0;
public:
/// Reference to returned data (RETURN opcode used)
bytes_ref returnData;
};
class JIT
{
@ -23,14 +153,23 @@ public:
/// Returns `true` if the EVM code has been compiled and loaded into memory.
/// In this case the code can be executed without overhead.
/// \param _codeHash The Keccak hash of the EVM code.
static bool isCodeReady(h256 _codeHash);
private:
friend class dev::eth::jit::ExecutionEngine;
EXPORT static bool isCodeReady(h256 const& _codeHash);
static uint64_t getCode(h256 _codeHash);
static void mapCode(h256 _codeHash, uint64_t _funcAddr);
EXPORT static ReturnCode exec(ExecutionContext& _context);
};
}
}
namespace std
{
template<> struct hash<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]);
};
};
}

17
evmjit/libevmjit-cpp/Env.cpp

@ -3,7 +3,6 @@
#include <libdevcore/SHA3.h>
#include <libevmcore/Params.h>
#include <libevm/ExtVMFace.h>
#include <evmjit/DataTypes.h>
#include "Utils.h"
@ -21,15 +20,15 @@ extern "C"
EXPORT void env_sload(ExtVMFace* _env, i256* _index, i256* o_value)
{
auto index = llvm2eth(*_index);
auto index = jit2eth(*_index);
auto value = _env->store(index); // Interface uses native endianness
*o_value = eth2llvm(value);
*o_value = eth2jit(value);
}
EXPORT void env_sstore(ExtVMFace* _env, i256* _index, i256* _value)
{
auto index = llvm2eth(*_index);
auto value = llvm2eth(*_value);
auto index = jit2eth(*_index);
auto value = jit2eth(*_value);
if (value == 0 && _env->store(index) != 0) // If delete
_env->sub.refunds += c_sstoreRefundGas; // Increase refund counter
@ -40,17 +39,17 @@ extern "C"
EXPORT void env_balance(ExtVMFace* _env, h256* _address, i256* o_value)
{
auto u = _env->balance(right160(*_address));
*o_value = eth2llvm(u);
*o_value = eth2jit(u);
}
EXPORT void env_blockhash(ExtVMFace* _env, i256* _number, h256* o_hash)
{
*o_hash = _env->blockhash(llvm2eth(*_number));
*o_hash = _env->blockhash(jit2eth(*_number));
}
EXPORT void env_create(ExtVMFace* _env, int64_t* io_gas, i256* _endowment, byte* _initBeg, uint64_t _initSize, h256* o_address)
{
auto endowment = llvm2eth(*_endowment);
auto endowment = jit2eth(*_endowment);
if (_env->balance(_env->myAddress) >= endowment && _env->depth < 1024)
{
u256 gas = *io_gas;
@ -65,7 +64,7 @@ extern "C"
EXPORT bool env_call(ExtVMFace* _env, int64_t* io_gas, int64_t _callGas, h256* _receiveAddress, i256* _value, byte* _inBeg, uint64_t _inSize, byte* _outBeg, uint64_t _outSize, h256* _codeAddress)
{
CallParameters params;
params.value = llvm2eth(*_value);
params.value = jit2eth(*_value);
params.senderAddress = _env->myAddress;
params.receiveAddress = right160(*_receiveAddress);
params.codeAddress = right160(*_codeAddress);

41
evmjit/libevmjit-cpp/JitVM.cpp

@ -7,7 +7,6 @@
#include <libdevcore/SHA3.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <evmjit/libevmjit/ExecutionEngine.h>
#include "Utils.h"
@ -20,8 +19,6 @@ extern "C" void env_sload(); // fake declaration for linker symbol stripping wor
bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _onOp)
{
using namespace jit;
auto rejected = false;
// TODO: Rejecting transactions with gas limit > 2^63 can be used by attacker to take JIT out of scope
rejected |= io_gas > std::numeric_limits<decltype(m_data.gas)>::max(); // Do not accept requests with gas > 2^63 (int64 max)
@ -40,36 +37,38 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on
m_data.gasPrice = static_cast<decltype(m_data.gasPrice)>(_ext.gasPrice);
m_data.callData = _ext.data.data();
m_data.callDataSize = _ext.data.size();
m_data.address = eth2llvm(fromAddress(_ext.myAddress));
m_data.caller = eth2llvm(fromAddress(_ext.caller));
m_data.origin = eth2llvm(fromAddress(_ext.origin));
m_data.callValue = eth2llvm(_ext.value);
m_data.coinBase = eth2llvm(fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.difficulty = eth2llvm(_ext.currentBlock.difficulty);
m_data.gasLimit = eth2llvm(_ext.currentBlock.gasLimit);
m_data.address = eth2jit(fromAddress(_ext.myAddress));
m_data.caller = eth2jit(fromAddress(_ext.caller));
m_data.origin = eth2jit(fromAddress(_ext.origin));
m_data.callValue = eth2jit(_ext.value);
m_data.coinBase = eth2jit(fromAddress(_ext.currentBlock.coinbaseAddress));
m_data.difficulty = eth2jit(_ext.currentBlock.difficulty);
m_data.gasLimit = eth2jit(_ext.currentBlock.gasLimit);
m_data.number = static_cast<decltype(m_data.number)>(_ext.currentBlock.number);
m_data.timestamp = static_cast<decltype(m_data.timestamp)>(_ext.currentBlock.timestamp);
m_data.code = _ext.code.data();
m_data.codeSize = _ext.code.size();
m_data.codeHash = eth2llvm(_ext.codeHash);
m_data.codeHash = eth2jit(_ext.codeHash);
auto env = reinterpret_cast<Env*>(&_ext);
auto exitCode = m_engine.run(&m_data, env);
// Pass pointer to ExtVMFace casted to evmjit::Env* opaque type.
// JIT will do nothing with the pointer, just pass it to Env callback functions implemented in Env.cpp.
m_context.init(m_data, reinterpret_cast<evmjit::Env*>(&_ext));
auto exitCode = evmjit::JIT::exec(m_context);
switch (exitCode)
{
case ReturnCode::Suicide:
_ext.suicide(right160(llvm2eth(m_data.address)));
case evmjit::ReturnCode::Suicide:
_ext.suicide(right160(jit2eth(m_data.address)));
break;
case ReturnCode::BadJumpDestination:
case evmjit::ReturnCode::BadJumpDestination:
BOOST_THROW_EXCEPTION(BadJumpDestination());
case ReturnCode::OutOfGas:
case evmjit::ReturnCode::OutOfGas:
BOOST_THROW_EXCEPTION(OutOfGas());
case ReturnCode::StackUnderflow:
case evmjit::ReturnCode::StackUnderflow: // FIXME: Remove support for detail errors
BOOST_THROW_EXCEPTION(StackUnderflow());
case ReturnCode::BadInstruction:
case evmjit::ReturnCode::BadInstruction:
BOOST_THROW_EXCEPTION(BadInstruction());
case ReturnCode::LinkerWorkaround: // never happens
case evmjit::ReturnCode::LinkerWorkaround: // never happens
env_sload(); // but forces linker to include env_* JIT callback functions
break;
default:
@ -77,7 +76,7 @@ bytesConstRef JitVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _on
}
io_gas = m_data.gas;
return {std::get<0>(m_engine.returnData), std::get<1>(m_engine.returnData)};
return {std::get<0>(m_context.returnData), std::get<1>(m_context.returnData)};
}
}

6
evmjit/libevmjit-cpp/JitVM.h

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

29
evmjit/libevmjit-cpp/Utils.h

@ -1,40 +1,41 @@
#pragma once
#include <evmjit/DataTypes.h>
#include <evmjit/JIT.h>
namespace dev
{
namespace eth
{
inline u256 llvm2eth(evmjit::i256 _i)
/// Converts EVM JIT representation of 256-bit integer to eth type dev::u256.
inline u256 jit2eth(evmjit::i256 _i)
{
u256 u = 0;
u |= _i.d;
u256 u = _i.words[3];
u <<= 64;
u |= _i.c;
u |= _i.words[2];
u <<= 64;
u |= _i.b;
u |= _i.words[1];
u <<= 64;
u |= _i.a;
u |= _i.words[0];
return u;
}
inline evmjit::i256 eth2llvm(u256 _u)
/// Converts eth type dev::u256 to EVM JIT representation of 256-bit integer.
inline evmjit::i256 eth2jit(u256 _u)
{
evmjit::i256 i;
u256 mask = 0xFFFFFFFFFFFFFFFF;
i.a = static_cast<uint64_t>(_u & mask);
i.words[0] = static_cast<uint64_t>(_u);
_u >>= 64;
i.b = static_cast<uint64_t>(_u & mask);
i.words[1] = static_cast<uint64_t>(_u);
_u >>= 64;
i.c = static_cast<uint64_t>(_u & mask);
i.words[2] = static_cast<uint64_t>(_u);
_u >>= 64;
i.d = static_cast<uint64_t>(_u & mask);
i.words[3] = static_cast<uint64_t>(_u);
return i;
}
inline evmjit::h256 eth2llvm(h256 _u)
/// Converts eth type dev::h256 to EVM JIT representation of 256-bit hash value.
inline evmjit::h256 eth2jit(h256 _u)
{
/// Just directly copies memory
return *(evmjit::h256*)&_u;

674
evmjit/libevmjit/Arith256.cpp

@ -4,6 +4,7 @@
#include <iomanip>
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/Module.h>
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
@ -32,186 +33,364 @@ void Arith256::debug(llvm::Value* _value, char _c)
createCall(m_debug, {m_builder.CreateZExtOrTrunc(_value, Type::Word), m_builder.getInt8(_c)});
}
llvm::Function* Arith256::getMulFunc()
llvm::Function* Arith256::getMulFunc(llvm::Module& _module)
{
auto& func = m_mul;
if (!func)
{
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mul", getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
static const auto funcName = "evm.mul.i256";
if (auto func = _module.getFunction(funcName))
return func;
llvm::Type* argTypes[] = {Type::Word, Type::Word};
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto i64 = Type::Size;
auto i128 = builder.getIntNTy(128);
auto i256 = Type::Word;
auto c64 = Constant::get(64);
auto c128 = Constant::get(128);
auto c192 = Constant::get(192);
auto x_lo = builder.CreateTrunc(x, i64, "x.lo");
auto y_lo = builder.CreateTrunc(y, i64, "y.lo");
auto x_mi = builder.CreateTrunc(builder.CreateLShr(x, c64), i64);
auto y_mi = builder.CreateTrunc(builder.CreateLShr(y, c64), i64);
auto x_hi = builder.CreateTrunc(builder.CreateLShr(x, c128), i128);
auto y_hi = builder.CreateTrunc(builder.CreateLShr(y, c128), i128);
auto t1 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_lo, i128));
auto t2 = builder.CreateMul(builder.CreateZExt(x_lo, i128), builder.CreateZExt(y_mi, i128));
auto t3 = builder.CreateMul(builder.CreateZExt(x_lo, i128), y_hi);
auto t4 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_lo, i128));
auto t5 = builder.CreateMul(builder.CreateZExt(x_mi, i128), builder.CreateZExt(y_mi, i128));
auto t6 = builder.CreateMul(builder.CreateZExt(x_mi, i128), y_hi);
auto t7 = builder.CreateMul(x_hi, builder.CreateZExt(y_lo, i128));
auto t8 = builder.CreateMul(x_hi, builder.CreateZExt(y_mi, i128));
auto p = builder.CreateZExt(t1, i256);
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i256), c64));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i256), c64));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t5, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t6, i256), c192));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t7, i256), c128));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t8, i256), c192));
builder.CreateRet(p);
return func;
}
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
llvm::Function* Arith256::getMul512Func(llvm::Module& _module)
{
static const auto funcName = "evm.mul.i512";
if (auto func = _module.getFunction(funcName))
return func;
auto i512Ty = llvm::IntegerType::get(_module.getContext(), 512);
auto func = llvm::Function::Create(llvm::FunctionType::get(i512Ty, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto i128 = builder.getIntNTy(128);
auto i256 = Type::Word;
auto x_lo = builder.CreateZExt(builder.CreateTrunc(x, i128, "x.lo"), i256);
auto y_lo = builder.CreateZExt(builder.CreateTrunc(y, i128, "y.lo"), i256);
auto x_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256);
auto y_hi = builder.CreateZExt(builder.CreateTrunc(builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256);
auto mul256Func = getMulFunc(_module);
auto t1 = builder.CreateCall(mul256Func, {x_lo, y_lo});
auto t2 = builder.CreateCall(mul256Func, {x_lo, y_hi});
auto t3 = builder.CreateCall(mul256Func, {x_hi, y_lo});
auto t4 = builder.CreateCall(mul256Func, {x_hi, y_hi});
auto p = builder.CreateZExt(t1, i512Ty);
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t2, i512Ty), builder.getIntN(512, 128)));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t3, i512Ty), builder.getIntN(512, 128)));
p = builder.CreateAdd(p, builder.CreateShl(builder.CreateZExt(t4, i512Ty), builder.getIntN(512, 256)));
builder.CreateRet(p);
InsertPointGuard guard{m_builder};
auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(bb);
auto i64 = Type::Size;
auto i128 = m_builder.getIntNTy(128);
auto i256 = Type::Word;
auto c64 = Constant::get(64);
auto c128 = Constant::get(128);
auto c192 = Constant::get(192);
auto x_lo = m_builder.CreateTrunc(x, i64, "x.lo");
auto y_lo = m_builder.CreateTrunc(y, i64, "y.lo");
auto x_mi = m_builder.CreateTrunc(m_builder.CreateLShr(x, c64), i64);
auto y_mi = m_builder.CreateTrunc(m_builder.CreateLShr(y, c64), i64);
auto x_hi = m_builder.CreateTrunc(m_builder.CreateLShr(x, c128), i128);
auto y_hi = m_builder.CreateTrunc(m_builder.CreateLShr(y, c128), i128);
auto t1 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_lo, i128));
auto t2 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), m_builder.CreateZExt(y_mi, i128));
auto t3 = m_builder.CreateMul(m_builder.CreateZExt(x_lo, i128), y_hi);
auto t4 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_lo, i128));
auto t5 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), m_builder.CreateZExt(y_mi, i128));
auto t6 = m_builder.CreateMul(m_builder.CreateZExt(x_mi, i128), y_hi);
auto t7 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_lo, i128));
auto t8 = m_builder.CreateMul(x_hi, m_builder.CreateZExt(y_mi, i128));
auto p = m_builder.CreateZExt(t1, i256);
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i256), c64));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i256), c64));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t5, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t6, i256), c192));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t7, i256), c128));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t8, i256), c192));
m_builder.CreateRet(p);
}
return func;
}
llvm::Function* Arith256::getMul512Func()
namespace
{
auto& func = m_mul512;
if (!func)
{
auto i512 = m_builder.getIntNTy(512);
llvm::Type* argTypes[] = {Type::Word, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(i512, argTypes, false), llvm::Function::PrivateLinkage, "mul512", getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
llvm::Function* createUDivRemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName)
{
// Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research
// The following algorithm also handles divisor of value 0 returning 0 for both quotient and remainder
auto retType = llvm::VectorType::get(_type, 2);
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto zero = llvm::ConstantInt::get(_type, 0);
auto one = llvm::ConstantInt::get(_type, 1);
auto x = &func->getArgumentList().front();
x->setName("x");
auto yArg = x->getNextNode();
yArg->setName("y");
auto entryBB = llvm::BasicBlock::Create(_module.getContext(), "Entry", func);
auto mainBB = llvm::BasicBlock::Create(_module.getContext(), "Main", func);
auto loopBB = llvm::BasicBlock::Create(_module.getContext(), "Loop", func);
auto continueBB = llvm::BasicBlock::Create(_module.getContext(), "Continue", func);
auto returnBB = llvm::BasicBlock::Create(_module.getContext(), "Return", func);
auto builder = llvm::IRBuilder<>{entryBB};
auto yLEx = builder.CreateICmpULE(yArg, x);
auto r0 = x;
builder.CreateCondBr(yLEx, mainBB, returnBB);
builder.SetInsertPoint(mainBB);
auto ctlzIntr = llvm::Intrinsic::getDeclaration(&_module, llvm::Intrinsic::ctlz, _type);
// both y and r are non-zero
auto yLz = builder.CreateCall(ctlzIntr, {yArg, builder.getInt1(true)}, "y.lz");
auto rLz = builder.CreateCall(ctlzIntr, {r0, builder.getInt1(true)}, "r.lz");
auto i0 = builder.CreateNUWSub(yLz, rLz, "i0");
auto y0 = builder.CreateShl(yArg, i0);
builder.CreateBr(loopBB);
builder.SetInsertPoint(loopBB);
auto yPhi = builder.CreatePHI(_type, 2, "y.phi");
auto rPhi = builder.CreatePHI(_type, 2, "r.phi");
auto iPhi = builder.CreatePHI(_type, 2, "i.phi");
auto qPhi = builder.CreatePHI(_type, 2, "q.phi");
auto rUpdate = builder.CreateNUWSub(rPhi, yPhi);
auto qUpdate = builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0
auto rGEy = builder.CreateICmpUGE(rPhi, yPhi);
auto r1 = builder.CreateSelect(rGEy, rUpdate, rPhi, "r1");
auto q1 = builder.CreateSelect(rGEy, qUpdate, qPhi, "q");
auto iZero = builder.CreateICmpEQ(iPhi, zero);
builder.CreateCondBr(iZero, returnBB, continueBB);
builder.SetInsertPoint(continueBB);
auto i2 = builder.CreateNUWSub(iPhi, one);
auto q2 = builder.CreateShl(q1, one);
auto y2 = builder.CreateLShr(yPhi, one);
builder.CreateBr(loopBB);
yPhi->addIncoming(y0, mainBB);
yPhi->addIncoming(y2, continueBB);
rPhi->addIncoming(r0, mainBB);
rPhi->addIncoming(r1, continueBB);
iPhi->addIncoming(i0, mainBB);
iPhi->addIncoming(i2, continueBB);
qPhi->addIncoming(zero, mainBB);
qPhi->addIncoming(q2, continueBB);
builder.SetInsertPoint(returnBB);
auto qRet = builder.CreatePHI(_type, 2, "q.ret");
qRet->addIncoming(zero, entryBB);
qRet->addIncoming(q1, loopBB);
auto rRet = builder.CreatePHI(_type, 2, "r.ret");
rRet->addIncoming(r0, entryBB);
rRet->addIncoming(r1, loopBB);
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), qRet, uint64_t(0), "ret0");
ret = builder.CreateInsertElement(ret, rRet, 1, "ret");
builder.CreateRet(ret);
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
return func;
}
}
llvm::Function* Arith256::getUDivRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.udivrem.i256";
if (auto func = _module.getFunction(funcName))
return func;
return createUDivRemFunc(Type::Word, _module, funcName);
}
llvm::Function* Arith256::getUDivRem512Func(llvm::Module& _module)
{
static const auto funcName = "evm.udivrem.i512";
if (auto func = _module.getFunction(funcName))
return func;
return createUDivRemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName);
}
llvm::Function* Arith256::getUDiv256Func(llvm::Module& _module)
{
static const auto funcName = "evm.udiv.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto udivremFunc = getUDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto udivrem = builder.CreateCall(udivremFunc, {x, y});
auto udiv = builder.CreateExtractElement(udivrem, uint64_t(0));
builder.CreateRet(udiv);
InsertPointGuard guard{m_builder};
auto bb = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(bb);
auto i128 = m_builder.getIntNTy(128);
auto i256 = Type::Word;
auto x_lo = m_builder.CreateZExt(m_builder.CreateTrunc(x, i128, "x.lo"), i256);
auto y_lo = m_builder.CreateZExt(m_builder.CreateTrunc(y, i128, "y.lo"), i256);
auto x_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(x, Constant::get(128)), i128, "x.hi"), i256);
auto y_hi = m_builder.CreateZExt(m_builder.CreateTrunc(m_builder.CreateLShr(y, Constant::get(128)), i128, "y.hi"), i256);
auto t1 = createCall(getMulFunc(), {x_lo, y_lo});
auto t2 = createCall(getMulFunc(), {x_lo, y_hi});
auto t3 = createCall(getMulFunc(), {x_hi, y_lo});
auto t4 = createCall(getMulFunc(), {x_hi, y_hi});
auto p = m_builder.CreateZExt(t1, i512);
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t2, i512), m_builder.getIntN(512, 128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t3, i512), m_builder.getIntN(512, 128)));
p = m_builder.CreateAdd(p, m_builder.CreateShl(m_builder.CreateZExt(t4, i512), m_builder.getIntN(512, 256)));
m_builder.CreateRet(p);
}
return func;
}
llvm::Function* Arith256::getDivFunc(llvm::Type* _type)
namespace
{
llvm::Function* createURemFunc(llvm::Type* _type, llvm::Module& _module, char const* _funcName)
{
auto& func = _type == Type::Word ? m_div : m_div512;
auto udivremFunc = _type == Type::Word ? Arith256::getUDivRem256Func(_module) : Arith256::getUDivRem512Func(_module);
if (!func)
{
// Based of "Improved shift divisor algorithm" from "Software Integer Division" by Microsoft Research
// The following algorithm also handles divisor of value 0 returning 0 for both quotient and reminder
auto func = llvm::Function::Create(llvm::FunctionType::get(_type, {_type, _type}, false), llvm::Function::PrivateLinkage, _funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
llvm::Type* argTypes[] = {_type, _type};
auto retType = llvm::StructType::get(m_builder.getContext(), llvm::ArrayRef<llvm::Type*>{argTypes});
auto funcName = _type == Type::Word ? "div" : "div512";
func = llvm::Function::Create(llvm::FunctionType::get(retType, argTypes, false), llvm::Function::PrivateLinkage, funcName, getModule());
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto zero = llvm::ConstantInt::get(_type, 0);
auto one = llvm::ConstantInt::get(_type, 1);
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto udivrem = builder.CreateCall(udivremFunc, {x, y});
auto r = builder.CreateExtractElement(udivrem, uint64_t(1));
builder.CreateRet(r);
auto x = &func->getArgumentList().front();
x->setName("x");
auto yArg = x->getNextNode();
yArg->setName("y");
return func;
}
}
InsertPointGuard guard{m_builder};
llvm::Function* Arith256::getURem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.urem.i256";
if (auto func = _module.getFunction(funcName))
return func;
return createURemFunc(Type::Word, _module, funcName);
}
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), "Entry", func);
auto mainBB = llvm::BasicBlock::Create(m_builder.getContext(), "Main", func);
auto loopBB = llvm::BasicBlock::Create(m_builder.getContext(), "Loop", func);
auto continueBB = llvm::BasicBlock::Create(m_builder.getContext(), "Continue", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
llvm::Function* Arith256::getURem512Func(llvm::Module& _module)
{
static const auto funcName = "evm.urem.i512";
if (auto func = _module.getFunction(funcName))
return func;
return createURemFunc(llvm::IntegerType::get(_module.getContext(), 512), _module, funcName);
}
m_builder.SetInsertPoint(entryBB);
auto yNonZero = m_builder.CreateICmpNE(yArg, zero);
auto yLEx = m_builder.CreateICmpULE(yArg, x);
auto r0 = m_builder.CreateSelect(yNonZero, x, zero, "r0");
m_builder.CreateCondBr(m_builder.CreateAnd(yLEx, yNonZero), mainBB, returnBB);
m_builder.SetInsertPoint(mainBB);
auto ctlzIntr = llvm::Intrinsic::getDeclaration(getModule(), llvm::Intrinsic::ctlz, _type);
// both y and r are non-zero
auto yLz = m_builder.CreateCall(ctlzIntr, {yArg, m_builder.getInt1(true)}, "y.lz");
auto rLz = m_builder.CreateCall(ctlzIntr, {r0, m_builder.getInt1(true)}, "r.lz");
auto i0 = m_builder.CreateNUWSub(yLz, rLz, "i0");
auto y0 = m_builder.CreateShl(yArg, i0);
m_builder.CreateBr(loopBB);
m_builder.SetInsertPoint(loopBB);
auto yPhi = m_builder.CreatePHI(_type, 2, "y.phi");
auto rPhi = m_builder.CreatePHI(_type, 2, "r.phi");
auto iPhi = m_builder.CreatePHI(_type, 2, "i.phi");
auto qPhi = m_builder.CreatePHI(_type, 2, "q.phi");
auto rUpdate = m_builder.CreateNUWSub(rPhi, yPhi);
auto qUpdate = m_builder.CreateOr(qPhi, one); // q += 1, q lowest bit is 0
auto rGEy = m_builder.CreateICmpUGE(rPhi, yPhi);
auto r1 = m_builder.CreateSelect(rGEy, rUpdate, rPhi, "r1");
auto q1 = m_builder.CreateSelect(rGEy, qUpdate, qPhi, "q");
auto iZero = m_builder.CreateICmpEQ(iPhi, zero);
m_builder.CreateCondBr(iZero, returnBB, continueBB);
llvm::Function* Arith256::getSDivRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.sdivrem.i256";
if (auto func = _module.getFunction(funcName))
return func;
m_builder.SetInsertPoint(continueBB);
auto i2 = m_builder.CreateNUWSub(iPhi, one);
auto q2 = m_builder.CreateShl(q1, one);
auto y2 = m_builder.CreateLShr(yPhi, one);
m_builder.CreateBr(loopBB);
yPhi->addIncoming(y0, mainBB);
yPhi->addIncoming(y2, continueBB);
rPhi->addIncoming(r0, mainBB);
rPhi->addIncoming(r1, continueBB);
iPhi->addIncoming(i0, mainBB);
iPhi->addIncoming(i2, continueBB);
qPhi->addIncoming(zero, mainBB);
qPhi->addIncoming(q2, continueBB);
auto udivremFunc = getUDivRem256Func(_module);
auto retType = llvm::VectorType::get(Type::Word, 2);
auto func = llvm::Function::Create(llvm::FunctionType::get(retType, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), "", func);
auto builder = llvm::IRBuilder<>{bb};
auto xIsNeg = builder.CreateICmpSLT(x, Constant::get(0));
auto xNeg = builder.CreateSub(Constant::get(0), x);
auto xAbs = builder.CreateSelect(xIsNeg, xNeg, x);
auto yIsNeg = builder.CreateICmpSLT(y, Constant::get(0));
auto yNeg = builder.CreateSub(Constant::get(0), y);
auto yAbs = builder.CreateSelect(yIsNeg, yNeg, y);
auto res = builder.CreateCall(udivremFunc, {xAbs, yAbs});
auto qAbs = builder.CreateExtractElement(res, uint64_t(0));
auto rAbs = builder.CreateExtractElement(res, 1);
// the remainder has the same sign as dividend
auto rNeg = builder.CreateSub(Constant::get(0), rAbs);
auto r = builder.CreateSelect(xIsNeg, rNeg, rAbs);
auto qNeg = builder.CreateSub(Constant::get(0), qAbs);
auto xyOpposite = builder.CreateXor(xIsNeg, yIsNeg);
auto q = builder.CreateSelect(xyOpposite, qNeg, qAbs);
auto ret = builder.CreateInsertElement(llvm::UndefValue::get(retType), q, uint64_t(0));
ret = builder.CreateInsertElement(ret, r, 1);
builder.CreateRet(ret);
return func;
}
llvm::Function* Arith256::getSDiv256Func(llvm::Module& _module)
{
static const auto funcName = "evm.sdiv.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto sdivremFunc = getSDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y});
auto q = builder.CreateExtractElement(sdivrem, uint64_t(0));
builder.CreateRet(q);
return func;
}
llvm::Function* Arith256::getSRem256Func(llvm::Module& _module)
{
static const auto funcName = "evm.srem.i256";
if (auto func = _module.getFunction(funcName))
return func;
auto sdivremFunc = getSDivRem256Func(_module);
auto func = llvm::Function::Create(llvm::FunctionType::get(Type::Word, {Type::Word, Type::Word}, false), llvm::Function::PrivateLinkage, funcName, &_module);
func->setDoesNotThrow();
func->setDoesNotAccessMemory();
auto x = &func->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto bb = llvm::BasicBlock::Create(_module.getContext(), {}, func);
auto builder = llvm::IRBuilder<>{bb};
auto sdivrem = builder.CreateCall(sdivremFunc, {x, y});
auto r = builder.CreateExtractElement(sdivrem, uint64_t(1));
builder.CreateRet(r);
m_builder.SetInsertPoint(returnBB);
auto qRet = m_builder.CreatePHI(_type, 2, "q.ret");
qRet->addIncoming(zero, entryBB);
qRet->addIncoming(q1, loopBB);
auto rRet = m_builder.CreatePHI(_type, 2, "r.ret");
rRet->addIncoming(r0, entryBB);
rRet->addIncoming(r1, loopBB);
auto ret = m_builder.CreateInsertValue(llvm::UndefValue::get(retType), qRet, 0, "ret0");
ret = m_builder.CreateInsertValue(ret, rRet, 1, "ret");
m_builder.CreateRet(ret);
}
return func;
}
@ -260,14 +439,15 @@ llvm::Function* Arith256::getExpFunc()
m_builder.CreateCondBr(eOdd, updateBB, continueBB);
m_builder.SetInsertPoint(updateBB);
auto r0 = createCall(getMulFunc(), {r, b});
auto mul256Func = getMulFunc(*getModule());
auto r0 = createCall(mul256Func, {r, b});
m_builder.CreateBr(continueBB);
m_builder.SetInsertPoint(continueBB);
auto r1 = m_builder.CreatePHI(Type::Word, 2, "r1");
r1->addIncoming(r, bodyBB);
r1->addIncoming(r0, updateBB);
auto b1 = createCall(getMulFunc(), {b, b});
auto b1 = createCall(mul256Func, {b, b});
auto e1 = m_builder.CreateLShr(e, Constant::get(1), "e1");
m_builder.CreateBr(headerBB);
@ -284,137 +464,6 @@ llvm::Function* Arith256::getExpFunc()
return m_exp;
}
llvm::Function* Arith256::getAddModFunc()
{
if (!m_addmod)
{
auto i512Ty = m_builder.getIntNTy(512);
llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word};
m_addmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "addmod", getModule());
m_addmod->setDoesNotThrow();
m_addmod->setDoesNotAccessMemory();
auto x = &m_addmod->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto mod = y->getNextNode();
mod->setName("m");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_addmod);
m_builder.SetInsertPoint(entryBB);
auto x512 = m_builder.CreateZExt(x, i512Ty, "x512");
auto y512 = m_builder.CreateZExt(y, i512Ty, "y512");
auto m512 = m_builder.CreateZExt(mod, i512Ty, "m512");
auto s = m_builder.CreateAdd(x512, y512, "s");
auto d = createCall(getDivFunc(i512Ty), {s, m512});
auto r = m_builder.CreateExtractValue(d, 1, "r");
m_builder.CreateRet(m_builder.CreateTrunc(r, Type::Word));
}
return m_addmod;
}
llvm::Function* Arith256::getMulModFunc()
{
if (!m_mulmod)
{
llvm::Type* argTypes[] = {Type::Word, Type::Word, Type::Word};
m_mulmod = llvm::Function::Create(llvm::FunctionType::get(Type::Word, argTypes, false), llvm::Function::PrivateLinkage, "mulmod", getModule());
m_mulmod->setDoesNotThrow();
m_mulmod->setDoesNotAccessMemory();
auto i512Ty = m_builder.getIntNTy(512);
auto x = &m_mulmod->getArgumentList().front();
x->setName("x");
auto y = x->getNextNode();
y->setName("y");
auto mod = y->getNextNode();
mod->setName("mod");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, m_mulmod);
m_builder.SetInsertPoint(entryBB);
auto p = createCall(getMul512Func(), {x, y});
auto m = m_builder.CreateZExt(mod, i512Ty, "m");
auto d = createCall(getDivFunc(i512Ty), {p, m});
auto r = m_builder.CreateExtractValue(d, 1, "r");
r = m_builder.CreateTrunc(r, Type::Word);
m_builder.CreateRet(r);
}
return m_mulmod;
}
llvm::Value* Arith256::mul(llvm::Value* _arg1, llvm::Value* _arg2)
{
if (auto c1 = llvm::dyn_cast<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)
{
// while (e != 0) {
@ -445,49 +494,6 @@ llvm::Value* Arith256::exp(llvm::Value* _arg1, llvm::Value* _arg2)
return createCall(getExpFunc(), {_arg1, _arg2});
}
llvm::Value* Arith256::addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3)
{
// FIXME: Disabled because of llvm::APInt::urem bug
// if (auto c1 = llvm::dyn_cast<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
evmjit/libevmjit/Arith256.h

@ -14,30 +14,25 @@ class Arith256 : public CompilerHelper
public:
Arith256(llvm::IRBuilder<>& _builder);
llvm::Value* mul(llvm::Value* _arg1, llvm::Value* _arg2);
std::pair<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* mulmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3);
llvm::Value* addmod(llvm::Value* _arg1, llvm::Value* _arg2, llvm::Value* _arg3);
void debug(llvm::Value* _value, char _c);
static llvm::Function* getMulFunc(llvm::Module& _module);
static llvm::Function* getMul512Func(llvm::Module& _module);
static llvm::Function* getUDiv256Func(llvm::Module& _module);
static llvm::Function* getURem256Func(llvm::Module& _module);
static llvm::Function* getURem512Func(llvm::Module& _module);
static llvm::Function* getUDivRem256Func(llvm::Module& _module);
static llvm::Function* getSDiv256Func(llvm::Module& _module);
static llvm::Function* getSRem256Func(llvm::Module& _module);
static llvm::Function* getSDivRem256Func(llvm::Module& _module);
static llvm::Function* getUDivRem512Func(llvm::Module& _module);
private:
llvm::Function* getMulFunc();
llvm::Function* getMul512Func();
llvm::Function* getDivFunc(llvm::Type* _type);
llvm::Function* getExpFunc();
llvm::Function* getAddModFunc();
llvm::Function* getMulModFunc();
llvm::Function* m_mul = nullptr;
llvm::Function* m_mul512 = nullptr;
llvm::Function* m_div = nullptr;
llvm::Function* m_div512 = nullptr;
llvm::Function* m_exp = nullptr;
llvm::Function* m_addmod = nullptr;
llvm::Function* m_mulmod = nullptr;
llvm::Function* m_debug = nullptr;
};

3
evmjit/libevmjit/Array.cpp

@ -6,7 +6,6 @@
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Runtime.h"
#include "Utils.h"
namespace dev
@ -17,7 +16,6 @@ namespace jit
{
static const auto c_reallocStep = 1;
static const auto c_reallocMultipier = 2;
llvm::Value* LazyFunction::call(llvm::IRBuilder<>& _builder, std::initializer_list<llvm::Value*> const& _args, llvm::Twine const& _name)
{
@ -56,7 +54,6 @@ llvm::Function* Array::createArrayPushFunc()
m_builder.SetInsertPoint(reallocBB);
auto newCap = m_builder.CreateNUWAdd(cap, m_builder.getInt64(c_reallocStep), "newCap");
//newCap = m_builder.CreateNUWMul(newCap, m_builder.getInt64(c_reallocMultipier));
auto reallocSize = m_builder.CreateShl(newCap, 5, "reallocSize"); // size in bytes: newCap * 32
auto bytes = m_builder.CreateBitCast(data, Type::BytePtr, "bytes");
auto newBytes = m_reallocFunc.call(m_builder, {bytes, reallocSize}, "newBytes");

2
evmjit/libevmjit/BasicBlock.h

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

8
evmjit/libevmjit/CMakeLists.txt

@ -1,26 +1,22 @@
set(TARGET_NAME evmjit)
set(SOURCES
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h
Arith256.cpp Arith256.h
Array.cpp Array.h
BasicBlock.cpp BasicBlock.h
Cache.cpp Cache.h
Common.h
Common.h
Compiler.cpp Compiler.h
CompilerHelper.cpp CompilerHelper.h
${EVMJIT_INCLUDE_DIR}/evmjit/DataTypes.h
Endianness.cpp Endianness.h
ExecStats.cpp ExecStats.h
ExecutionEngine.cpp ExecutionEngine.h
Ext.cpp Ext.h
GasMeter.cpp GasMeter.h
Instruction.cpp Instruction.h
interface.cpp interface.h
JIT.cpp ${EVMJIT_INCLUDE_DIR}/evmjit/JIT.h
Memory.cpp Memory.h
Optimizer.cpp Optimizer.h
Runtime.cpp Runtime.h
RuntimeData.h
RuntimeManager.cpp RuntimeManager.h
Stack.cpp Stack.h
Type.cpp Type.h

27
evmjit/libevmjit/Cache.cpp

@ -12,15 +12,13 @@
#include <llvm/Support/raw_os_ostream.h>
#include "preprocessor/llvm_includes_end.h"
#include "ExecutionEngine.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev
{
namespace eth
{
namespace jit
namespace evmjit
{
namespace
@ -29,7 +27,7 @@ namespace
std::mutex x_cacheMutex;
CacheMode g_mode;
std::unique_ptr<llvm::MemoryBuffer> g_lastObject;
ExecutionEngineListener* g_listener;
JITListener* g_listener;
static const size_t c_versionStampLength = 32;
llvm::StringRef getLibVersionStamp()
@ -44,15 +42,25 @@ namespace
}
}
ObjectCache* Cache::getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener)
ObjectCache* Cache::init(CacheMode _mode, JITListener* _listener)
{
static ObjectCache objectCache;
Guard g{x_cacheMutex};
g_mode = _mode;
g_listener = _listener;
return &objectCache;
if (g_mode == CacheMode::clear)
{
Cache::clear();
g_mode = CacheMode::off;
}
if (g_mode != CacheMode::off)
{
static ObjectCache objectCache;
return &objectCache;
}
return nullptr;
}
void Cache::clear()
@ -185,4 +193,3 @@ std::unique_ptr<llvm::MemoryBuffer> ObjectCache::getObject(llvm::Module const* _
}
}
}

9
evmjit/libevmjit/Cache.h

@ -14,11 +14,9 @@ namespace llvm
namespace dev
{
namespace eth
namespace evmjit
{
namespace jit
{
class ExecutionEngineListener;
class JITListener;
enum class CacheMode
{
@ -47,7 +45,7 @@ public:
class Cache
{
public:
static ObjectCache* getObjectCache(CacheMode _mode, ExecutionEngineListener* _listener);
static ObjectCache* init(CacheMode _mode, JITListener* _listener);
static std::unique_ptr<llvm::Module> getObject(std::string const& id);
/// Clears cache storage
@ -59,4 +57,3 @@ public:
}
}
}

39
evmjit/libevmjit/Common.h

@ -1,53 +1,16 @@
#pragma once
#include <tuple>
#include <cstdint>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
namespace dev
{
namespace eth
{
namespace jit
namespace evmjit
{
using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
using code_iterator = byte const*;
enum class ReturnCode
{
// Success codes
Stop = 0,
Return = 1,
Suicide = 2,
// Standard error codes
OutOfGas = -1,
StackUnderflow = -2,
BadJumpDestination = -3,
BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
// Internal error codes
LLVMConfigError = -101,
LLVMCompileError = -102,
LLVMLinkError = -103,
UnexpectedException = -111,
LinkerWorkaround = -299,
};
#define UNTESTED assert(false)
}
}
}

152
evmjit/libevmjit/Compiler.cpp

@ -49,6 +49,11 @@ void Compiler::createBasicBlocks(code_iterator _codeBegin, code_iterator _codeEn
return _curr + offset;
};
// Skip all STOPs in the end
for (; _codeEnd != _codeBegin; --_codeEnd)
if (*(_codeEnd - 1) != static_cast<byte>(Instruction::STOP))
break;
auto begin = _codeBegin; // begin of current block
bool nextJumpDest = false;
for (auto curr = begin, next = begin; curr != _codeEnd; curr = next)
@ -159,10 +164,10 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
// TODO: Create Stop basic block on demand
m_stopBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Stop", m_mainFunc);
auto abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
m_abortBB = llvm::BasicBlock::Create(m_mainFunc->getContext(), "Abort", m_mainFunc);
auto firstBB = m_basicBlocks.empty() ? m_stopBB : m_basicBlocks.begin()->second.llvm();
m_builder.CreateCondBr(normalFlow, firstBB, abortBB, Type::expectTrue);
m_builder.CreateCondBr(normalFlow, firstBB, m_abortBB, Type::expectTrue);
for (auto basicBlockPairIt = m_basicBlocks.begin(); basicBlockPairIt != m_basicBlocks.end(); ++basicBlockPairIt)
{
@ -178,7 +183,7 @@ std::unique_ptr<llvm::Module> Compiler::compile(code_iterator _begin, code_itera
m_builder.SetInsertPoint(m_stopBB);
runtimeManager.exit(ReturnCode::Stop);
m_builder.SetInsertPoint(abortBB);
m_builder.SetInsertPoint(m_abortBB);
runtimeManager.exit(ReturnCode::OutOfGas);
removeDeadBlocks();
@ -270,44 +275,96 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.mul(lhs, rhs);
auto res = m_builder.CreateMul(lhs, rhs);
stack.push(res);
break;
}
case Instruction::DIV:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.div(lhs, rhs);
stack.push(res.first);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateUDiv(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
stack.push(r);
break;
}
case Instruction::SDIV:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.sdiv(lhs, rhs);
stack.push(res.first);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateSDiv(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
auto dNeg = m_builder.CreateSub(Constant::get(0), d);
r = m_builder.CreateSelect(divByMinusOne, dNeg, r); // protect against undef i256.min / -1
stack.push(r);
break;
}
case Instruction::MOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.div(lhs, rhs);
stack.push(res.second);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateURem(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
stack.push(r);
break;
}
case Instruction::SMOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto res = _arith.sdiv(lhs, rhs);
stack.push(res.second);
auto d = stack.pop();
auto n = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(n, Constant::get(0));
auto divByMinusOne = m_builder.CreateICmpEQ(n, Constant::get(-1));
n = m_builder.CreateSelect(divByZero, Constant::get(1), n); // protect against hardware signal
auto r = m_builder.CreateSRem(d, n);
r = m_builder.CreateSelect(divByZero, Constant::get(0), r);
r = m_builder.CreateSelect(divByMinusOne, Constant::get(0), r); // protect against undef i256.min / -1
stack.push(r);
break;
}
case Instruction::ADDMOD:
{
auto i512Ty = m_builder.getIntNTy(512);
auto a = stack.pop();
auto b = stack.pop();
auto m = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0));
a = m_builder.CreateZExt(a, i512Ty);
b = m_builder.CreateZExt(b, i512Ty);
m = m_builder.CreateZExt(m, i512Ty);
auto s = m_builder.CreateNUWAdd(a, b);
s = m_builder.CreateURem(s, m);
s = m_builder.CreateTrunc(s, Type::Word);
s = m_builder.CreateSelect(divByZero, Constant::get(0), s);
stack.push(s);
break;
}
case Instruction::MULMOD:
{
auto i512Ty = m_builder.getIntNTy(512);
auto a = stack.pop();
auto b = stack.pop();
auto m = stack.pop();
auto divByZero = m_builder.CreateICmpEQ(m, Constant::get(0));
m = m_builder.CreateZExt(m, i512Ty);
// TODO: Add support for i256 x i256 -> i512 in LowerEVM pass
llvm::Value* p = m_builder.CreateCall(Arith256::getMul512Func(*_basicBlock.llvm()->getParent()->getParent()), {a, b});
p = m_builder.CreateURem(p, m);
p = m_builder.CreateTrunc(p, Type::Word);
p = m_builder.CreateSelect(divByZero, Constant::get(0), p);
stack.push(p);
break;
}
@ -417,48 +474,29 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::BYTE:
{
const auto byteNum = stack.pop();
auto value = stack.pop();
const auto idx = stack.pop();
auto value = Endianness::toBE(m_builder, stack.pop());
value = Endianness::toBE(m_builder, value);
auto idxValid = m_builder.CreateICmpULT(idx, Constant::get(32), "idxValid");
auto bytes = m_builder.CreateBitCast(value, llvm::VectorType::get(Type::Byte, 32), "bytes");
auto safeByteNum = m_builder.CreateZExt(m_builder.CreateTrunc(byteNum, m_builder.getIntNTy(5)), Type::lowPrecision); // Trim index, large values can crash
auto byte = m_builder.CreateExtractElement(bytes, safeByteNum, "byte");
// TODO: Workaround for LLVM bug. Using big value of index causes invalid memory access.
auto safeIdx = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5));
// TODO: Workaround for LLVM bug. DAG Builder used sext on index instead of zext
safeIdx = m_builder.CreateZExt(safeIdx, Type::Size);
auto byte = m_builder.CreateExtractElement(bytes, safeIdx, "byte");
value = m_builder.CreateZExt(byte, Type::Word);
auto byteNumValid = m_builder.CreateICmpULT(byteNum, Constant::get(32));
value = m_builder.CreateSelect(byteNumValid, value, Constant::get(0));
value = m_builder.CreateSelect(idxValid, value, Constant::get(0));
stack.push(value);
break;
}
case Instruction::ADDMOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto mod = stack.pop();
auto res = _arith.addmod(lhs, rhs, mod);
stack.push(res);
break;
}
case Instruction::MULMOD:
{
auto lhs = stack.pop();
auto rhs = stack.pop();
auto mod = stack.pop();
auto res = _arith.mulmod(lhs, rhs, mod);
stack.push(res);
break;
}
case Instruction::SIGNEXTEND:
{
auto idx = stack.pop();
auto word = stack.pop();
auto k32_ = m_builder.CreateTrunc(idx, m_builder.getIntNTy(5), "k_32");
auto k32 = m_builder.CreateZExt(k32_, Type::lowPrecision);
auto k32 = m_builder.CreateZExt(k32_, Type::Size);
auto k32x8 = m_builder.CreateMul(k32, m_builder.getInt64(8), "kx8");
// test for word >> (k * 8 + 7)
@ -495,11 +533,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::POP:
{
auto val = stack.pop();
static_cast<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");
stack.pop();
break;
}
@ -656,7 +690,6 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
}
case Instruction::CODESIZE:
// TODO: Use constant
stack.push(_runtimeManager.getCodeSize());
break;
@ -729,8 +762,8 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::CALLDATALOAD:
{
auto index = stack.pop();
auto value = _ext.calldataload(index);
auto idx = stack.pop();
auto value = _ext.calldataload(idx);
stack.push(value);
break;
}
@ -797,7 +830,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
case Instruction::STOP:
{
m_builder.CreateRet(Constant::get(ReturnCode::Stop));
m_builder.CreateBr(m_stopBB);
break;
}
@ -824,7 +857,7 @@ void Compiler::compileBasicBlock(BasicBlock& _basicBlock, RuntimeManager& _runti
}
default: // Invalid instruction - abort
m_builder.CreateRet(Constant::get(ReturnCode::BadInstruction));
m_builder.CreateBr(m_abortBB);
it = _basicBlock.end() - 1; // finish block compilation
}
}
@ -942,4 +975,3 @@ void Compiler::dump()
}
}
}

4
evmjit/libevmjit/Compiler.h

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

25
evmjit/libevmjit/CompilerHelper.h

@ -37,7 +37,6 @@ protected:
friend class RuntimeHelper;
};
/// Compiler helper that depends on runtime data
class RuntimeHelper : public CompilerHelper
{
@ -50,29 +49,7 @@ private:
RuntimeManager& m_runtimeManager;
};
/// Saves the insert point of the IR builder and restores it when destructed
struct InsertPointGuard
{
InsertPointGuard(llvm::IRBuilder<>& _builder) :
m_builder(_builder),
m_insertBB(m_builder.GetInsertBlock()),
m_insertPt(m_builder.GetInsertPoint())
{}
InsertPointGuard(const InsertPointGuard&) = delete;
void operator=(InsertPointGuard) = delete;
~InsertPointGuard()
{
m_builder.SetInsertPoint(m_insertBB, m_insertPt);
}
private:
llvm::IRBuilder<>& m_builder;
llvm::BasicBlock* m_insertBB;
llvm::BasicBlock::iterator m_insertPt;
};
using InsertPointGuard = llvm::IRBuilderBase::InsertPointGuard;
}
}

5
evmjit/libevmjit/ExecStats.cpp

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

35
evmjit/libevmjit/ExecStats.h

@ -1,19 +1,43 @@
#pragma once
#include <memory>
#include <vector>
#include <string>
#include <chrono>
#include "ExecutionEngine.h"
namespace dev
{
namespace eth
namespace evmjit
{
enum class ExecState
{
namespace jit
Started,
CacheLoad,
CacheWrite,
Compilation,
Optimization,
CodeGen,
Execution,
Return,
Finished
};
class JITListener
{
public:
JITListener() = default;
JITListener(JITListener const&) = delete;
JITListener& operator=(JITListener) = delete;
virtual ~JITListener() {}
virtual void executionStarted() {}
virtual void executionEnded() {}
class ExecStats : public ExecutionEngineListener
virtual void stateChanged(ExecState) {}
};
class ExecStats : public JITListener
{
public:
using clock = std::chrono::high_resolution_clock;
@ -42,4 +66,3 @@ public:
}
}
}

192
evmjit/libevmjit/ExecutionEngine.cpp

@ -1,192 +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 "evmjit/JIT.h"
#include "Runtime.h"
#include "Compiler.h"
#include "Optimizer.h"
#include "Cache.h"
#include "ExecStats.h"
#include "Utils.h"
#include "BuildInfo.gen.h"
namespace dev
{
namespace eth
{
namespace jit
{
using evmjit::JIT;
namespace
{
using EntryFuncPtr = ReturnCode(*)(Runtime*);
std::string codeHash(i256 const& _hash)
{
static const auto size = sizeof(_hash);
static const auto hexChars = "0123456789abcdef";
std::string str;
str.resize(size * 2);
auto outIt = str.rbegin(); // reverse for BE
auto& arr = *(std::array<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");
}
}
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::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()));
// FIXME: LLVM 3.7: test on Windows
auto triple = llvm::Triple(llvm::sys::getProcessTriple());
if (triple.getOS() == llvm::Triple::OSType::Win32)
triple.setObjectFormat(llvm::Triple::ObjectFormatType::ELF); // MCJIT does not support COFF format
module->setTargetTriple(triple.str());
llvm::EngineBuilder builder(std::move(module));
builder.setEngineKind(llvm::EngineKind::JIT);
builder.setOptLevel(g_optimize ? llvm::CodeGenOpt::Default : llvm::CodeGenOpt::None);
ee.reset(builder.create());
if (!CHECK(ee))
return ReturnCode::LLVMConfigError;
ee->setObjectCache(objectCache);
// FIXME: Disabled during API changes
//if (preloadCache)
// Cache::preload(*ee, funcCache);
}
static StatsCollector statsCollector;
auto mainFuncName = codeHash(_data->codeHash);
m_runtime.init(_data, _env);
// TODO: Remove cast
auto entryFuncPtr = (EntryFuncPtr) JIT::getCode(_data->codeHash);
if (!entryFuncPtr)
{
auto module = objectCache ? Cache::getObject(mainFuncName) : nullptr;
if (!module)
{
listener->stateChanged(ExecState::Compilation);
assert(_data->code || !_data->codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler{{}}.compile(_data->code, _data->code + _data->codeSize, mainFuncName);
if (g_optimize)
{
listener->stateChanged(ExecState::Optimization);
optimize(*module);
}
}
if (g_dump)
module->dump();
ee->addModule(std::move(module));
listener->stateChanged(ExecState::CodeGen);
entryFuncPtr = (EntryFuncPtr)ee->getFunctionAddress(mainFuncName);
if (!CHECK(entryFuncPtr))
return ReturnCode::LLVMLinkError;
JIT::mapCode(_data->codeHash, (uint64_t)entryFuncPtr); // FIXME: Remove cast
}
listener->stateChanged(ExecState::Execution);
auto returnCode = entryFuncPtr(&m_runtime);
listener->stateChanged(ExecState::Return);
if (returnCode == ReturnCode::Return)
returnData = m_runtime.getReturnData(); // Save reference to return data
listener->stateChanged(ExecState::Finished);
if (g_stats)
statsCollector.stats.push_back(std::move(listener));
return returnCode;
}
}
}
}

59
evmjit/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
evmjit/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_blockhash", getFunctionType(Type::Void, {Type::EnvPtr, Type::WordPtr, Type::WordPtr})},
FuncDesc{"env_extcode", getFunctionType(Type::BytePtr, {Type::EnvPtr, Type::WordPtr, Type::Size->getPointerTo()})},
FuncDesc{"ext_calldataload", getFunctionType(Type::Void, {Type::RuntimeDataPtr, Type::WordPtr, Type::WordPtr})},
}};
return descs;
@ -101,12 +100,27 @@ void Ext::sstore(llvm::Value* _index, llvm::Value* _value)
createCall(EnvFunc::sstore, {getRuntimeManager().getEnvPtr(), byPtr(_index), byPtr(_value)}); // Uses native endianness
}
llvm::Value* Ext::calldataload(llvm::Value* _index)
llvm::Value* Ext::calldataload(llvm::Value* _idx)
{
auto ret = getArgAlloca();
createCall(EnvFunc::calldataload, {getRuntimeManager().getDataPtr(), byPtr(_index), ret});
ret = m_builder.CreateLoad(ret);
return Endianness::toNative(m_builder, ret);
auto result = m_builder.CreateBitCast(ret, Type::BytePtr);
auto callDataSize = getRuntimeManager().getCallDataSize();
auto callDataSize64 = m_builder.CreateTrunc(callDataSize, Type::Size);
auto idxValid = m_builder.CreateICmpULT(_idx, callDataSize);
auto idx = m_builder.CreateTrunc(m_builder.CreateSelect(idxValid, _idx, callDataSize), Type::Size, "idx");
auto end = m_builder.CreateNUWAdd(idx, m_builder.getInt64(32));
end = m_builder.CreateSelect(m_builder.CreateICmpULE(end, callDataSize64), end, callDataSize64);
auto copySize = m_builder.CreateNUWSub(end, idx);
auto padSize = m_builder.CreateNUWSub(m_builder.getInt64(32), copySize);
auto dataBegin = m_builder.CreateGEP(Type::Byte, getRuntimeManager().getCallData(), idx);
m_builder.CreateMemCpy(result, dataBegin, copySize, 1);
auto pad = m_builder.CreateGEP(Type::Byte, result, copySize);
m_builder.CreateMemSet(pad, m_builder.getInt8(0), padSize, 1);
m_argCounter = 0; // Release args allocas. TODO: This is a bad design
return Endianness::toNative(m_builder, m_builder.CreateLoad(ret));
}
llvm::Value* Ext::balance(llvm::Value* _address)

2
evmjit/libevmjit/Ext.h

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

1
evmjit/libevmjit/GasMeter.h

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

5
evmjit/libevmjit/Instruction.cpp

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

5
evmjit/libevmjit/Instruction.h

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

222
evmjit/libevmjit/JIT.cpp

@ -1,45 +1,239 @@
#include "evmjit/JIT.h"
#include <unordered_map>
#include <array>
#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*);
class JITImpl: JIT
std::string hash2str(i256 const& _hash)
{
public:
std::unordered_map<h256, uint64_t> codeMap;
static const auto size = sizeof(_hash);
static const auto hexChars = "0123456789abcdef";
std::string str;
str.resize(size * 2);
auto outIt = str.rbegin(); // reverse for BE
auto& arr = *(std::array<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;
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);
};
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
{
auto it = m_codeMap.find(_codeHash);
if (it != m_codeMap.end())
return it->second;
return nullptr;
}
void JITImpl::mapExecFunc(h256 _codeHash, ExecFunc _funcAddr)
{
m_codeMap.emplace(std::move(_codeHash), _funcAddr);
}
} // anonymous namespace
bool JIT::isCodeReady(h256 _codeHash)
bool JIT::isCodeReady(h256 const& _codeHash)
{
return JITImpl::instance().codeMap.count(_codeHash) != 0;
return JITImpl::instance().getExecFunc(_codeHash) != nullptr;
}
uint64_t JIT::getCode(h256 _codeHash)
ReturnCode JIT::exec(ExecutionContext& _context)
{
auto& codeMap = JITImpl::instance().codeMap;
auto it = codeMap.find(_codeHash);
if (it != codeMap.end())
return it->second;
return 0;
auto& jit = JITImpl::instance();
std::unique_ptr<ExecStats> listener{new ExecStats};
listener->stateChanged(ExecState::Started);
auto code = _context.code();
auto codeSize = _context.codeSize();
auto codeHash = _context.codeHash();
static StatsCollector statsCollector;
auto mainFuncName = hash2str(codeHash);
// TODO: Remove cast
auto execFunc = jit.getExecFunc(codeHash);
if (!execFunc)
{
auto module = Cache::getObject(mainFuncName);
if (!module)
{
listener->stateChanged(ExecState::Compilation);
assert(code || !codeSize); //TODO: Is it good idea to execute empty code?
module = Compiler{{}}.compile(code, code + codeSize, mainFuncName);
if (g_optimize)
{
listener->stateChanged(ExecState::Optimization);
optimize(*module);
}
prepare(*module);
}
if (g_dump)
module->dump();
jit.engine().addModule(std::move(module));
listener->stateChanged(ExecState::CodeGen);
execFunc = (ExecFunc)jit.engine().getFunctionAddress(mainFuncName);
if (!CHECK(execFunc))
return ReturnCode::LLVMLinkError;
jit.mapExecFunc(codeHash, execFunc);
}
listener->stateChanged(ExecState::Execution);
auto returnCode = execFunc(&_context);
listener->stateChanged(ExecState::Return);
if (returnCode == ReturnCode::Return)
_context.returnData = _context.getReturnData(); // Save reference to return data
listener->stateChanged(ExecState::Finished);
if (g_stats)
statsCollector.stats.push_back(std::move(listener));
return returnCode;
}
void JIT::mapCode(h256 _codeHash, uint64_t _funcAddr)
extern "C" void ext_free(void* _data) noexcept;
ExecutionContext::~ExecutionContext()
{
JITImpl::instance().codeMap.insert(std::make_pair(_codeHash, _funcAddr));
if (m_memData)
ext_free(m_memData); // Use helper free to check memory leaks
}
bytes_ref ExecutionContext::getReturnData() const
{
auto data = m_data->callData;
auto size = static_cast<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
evmjit/libevmjit/Memory.cpp

@ -5,7 +5,6 @@
#include "preprocessor/llvm_includes_end.h"
#include "Type.h"
#include "Runtime.h"
#include "GasMeter.h"
#include "Endianness.h"
#include "RuntimeManager.h"
@ -191,8 +190,7 @@ llvm::Value* Memory::getSize()
llvm::Value* Memory::getBytePtr(llvm::Value* _index)
{
auto idx = m_builder.CreateTrunc(_index, Type::Size, "idx"); // Never allow memory index be a type bigger than i64
return m_builder.CreateGEP(getData(), idx, "ptr");
return m_builder.CreateGEP(getData(), _index, "ptr");
}
void Memory::require(llvm::Value* _offset, llvm::Value* _size)
@ -235,7 +233,7 @@ void Memory::copyBytes(llvm::Value* _srcPtr, llvm::Value* _srcSize, llvm::Value*
auto bytesToZero = m_builder.CreateNUWSub(reqBytes, bytesToCopy, "bytesToZero");
auto src = m_builder.CreateGEP(_srcPtr, idx64, "src");
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx"); // Never allow memory index be a type bigger than i64
auto dstIdx = m_builder.CreateTrunc(_destMemIdx, Type::Size, "dstIdx");
auto padIdx = m_builder.CreateNUWAdd(dstIdx, bytesToCopy, "padIdx");
auto dst = m_memory.getPtr(getRuntimeManager().getMem(), dstIdx);
auto pad = m_memory.getPtr(getRuntimeManager().getMem(), padIdx);

97
evmjit/libevmjit/Optimizer.cpp

@ -1,11 +1,16 @@
#include "Optimizer.h"
#include "preprocessor/llvm_includes_start.h"
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/LegacyPassManager.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/IPO.h>
#include "preprocessor/llvm_includes_end.h"
#include "Arith256.h"
#include "Type.h"
namespace dev
{
namespace eth
@ -16,7 +21,7 @@ namespace jit
bool optimize(llvm::Module& _module)
{
auto pm = llvm::legacy::PassManager{};
//pm.add(llvm::createFunctionInliningPass(2, 2)); // Problem with APInt value bigger than 64bit
pm.add(llvm::createFunctionInliningPass(2, 2));
pm.add(llvm::createCFGSimplificationPass());
pm.add(llvm::createInstructionCombiningPass());
pm.add(llvm::createAggressiveDCEPass());
@ -24,6 +29,96 @@ bool optimize(llvm::Module& _module)
return pm.run(_module);
}
namespace
{
class LowerEVMPass: public llvm::BasicBlockPass
{
static char ID;
public:
LowerEVMPass():
llvm::BasicBlockPass(ID)
{}
virtual bool runOnBasicBlock(llvm::BasicBlock& _bb) override;
using llvm::BasicBlockPass::doFinalization;
virtual bool doFinalization(llvm::Module& _module) override;
};
char LowerEVMPass::ID = 0;
bool LowerEVMPass::runOnBasicBlock(llvm::BasicBlock& _bb)
{
auto modified = false;
auto module = _bb.getParent()->getParent();
auto i512Ty = llvm::IntegerType::get(_bb.getContext(), 512);
for (auto it = _bb.begin(); it != _bb.end(); )
{
auto& inst = *it++;
llvm::Function* func = nullptr;
if (inst.getType() == Type::Word)
{
switch (inst.getOpcode())
{
case llvm::Instruction::Mul:
func = Arith256::getMulFunc(*module);
break;
case llvm::Instruction::UDiv:
func = Arith256::getUDiv256Func(*module);
break;
case llvm::Instruction::URem:
func = Arith256::getURem256Func(*module);
break;
case llvm::Instruction::SDiv:
func = Arith256::getSDiv256Func(*module);
break;
case llvm::Instruction::SRem:
func = Arith256::getSRem256Func(*module);
break;
}
}
else if (inst.getType() == i512Ty)
{
switch (inst.getOpcode())
{
case llvm::Instruction::URem:
func = Arith256::getURem512Func(*module);
break;
}
}
if (func)
{
auto call = llvm::CallInst::Create(func, {inst.getOperand(0), inst.getOperand(1)}, "", &inst);
inst.replaceAllUsesWith(call);
inst.eraseFromParent();
modified = true;
}
}
return modified;
}
bool LowerEVMPass::doFinalization(llvm::Module&)
{
return false;
}
}
bool prepare(llvm::Module& _module)
{
auto pm = llvm::legacy::PassManager{};
pm.add(llvm::createDeadCodeEliminationPass());
pm.add(new LowerEVMPass{});
return pm.run(_module);
}
}
}
}

2
evmjit/libevmjit/Optimizer.h

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

43
evmjit/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
evmjit/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;
};
}
}
}

63
evmjit/libevmjit/RuntimeData.h

@ -1,63 +0,0 @@
#pragma once
#include "evmjit/DataTypes.h"
#include "Common.h"
namespace dev
{
namespace eth
{
namespace jit
{
using evmjit::i256;
using evmjit::h256;
struct RuntimeData
{
enum Index
{
Gas,
GasPrice,
CallData,
CallDataSize,
Address,
Caller,
Origin,
CallValue,
CoinBase,
Difficulty,
GasLimit,
Number,
Timestamp,
Code,
CodeSize,
SuicideDestAddress = Address, ///< Suicide balance destination address
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
};
int64_t gas = 0;
int64_t gasPrice = 0;
byte const* callData = nullptr;
uint64_t callDataSize = 0;
i256 address;
i256 caller;
i256 origin;
i256 callValue;
i256 coinBase;
i256 difficulty;
i256 gasLimit;
uint64_t number = 0;
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
h256 codeHash;
};
/// VM Environment (ExtVM) opaque type
struct Env;
}
}
}

56
evmjit/libevmjit/RuntimeManager.cpp

@ -64,22 +64,22 @@ llvm::Twine getName(RuntimeData::Index _index)
{
switch (_index)
{
default: return "data";
case RuntimeData::Address: return "address";
case RuntimeData::Caller: return "caller";
case RuntimeData::Origin: return "origin";
case RuntimeData::CallValue: return "callvalue";
case RuntimeData::GasPrice: return "gasprice";
case RuntimeData::CoinBase: return "coinbase";
case RuntimeData::Difficulty: return "difficulty";
case RuntimeData::GasLimit: return "gaslimit";
case RuntimeData::CallData: return "callData";
case RuntimeData::Code: return "code";
case RuntimeData::CodeSize: return "code";
case RuntimeData::CallDataSize: return "callDataSize";
case RuntimeData::Gas: return "gas";
case RuntimeData::Number: return "number";
case RuntimeData::Timestamp: return "timestamp";
default: return "";
case RuntimeData::Gas: return "msg.gas";
case RuntimeData::GasPrice: return "tx.gasprice";
case RuntimeData::CallData: return "msg.data.ptr";
case RuntimeData::CallDataSize: return "msg.data.size";
case RuntimeData::Address: return "this.address";
case RuntimeData::Caller: return "msg.caller";
case RuntimeData::Origin: return "tx.origin";
case RuntimeData::CallValue: return "msg.value";
case RuntimeData::CoinBase: return "block.coinbase";
case RuntimeData::Difficulty: return "block.difficulty";
case RuntimeData::GasLimit: return "block.gaslimit";
case RuntimeData::Number: return "block.number";
case RuntimeData::Timestamp: return "block.timestamp";
case RuntimeData::Code: return "code.ptr";
case RuntimeData::CodeSize: return "code.size";
}
}
}
@ -93,10 +93,8 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
// Unpack data
auto rtPtr = getRuntimePtr();
m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "data");
m_dataPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 0), "dataPtr");
assert(m_dataPtr->getType() == Type::RuntimeDataPtr);
m_gasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), m_dataPtr, 0, "gas");
assert(m_gasPtr->getType() == Type::Gas->getPointerTo());
m_memPtr = m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 2, "mem");
assert(m_memPtr->getType() == Array::getType()->getPointerTo());
m_envPtr = m_builder.CreateLoad(m_builder.CreateStructGEP(getRuntimeType(), rtPtr, 1), "env");
@ -105,6 +103,13 @@ RuntimeManager::RuntimeManager(llvm::IRBuilder<>& _builder, code_iterator _codeB
m_stackSize = m_builder.CreateAlloca(Type::Size, nullptr, "stackSize");
m_builder.CreateStore(m_builder.getInt64(0), m_stackSize);
auto data = m_builder.CreateLoad(m_dataPtr, "data");
for (unsigned i = 0; i < m_dataElts.size(); ++i)
m_dataElts[i] = m_builder.CreateExtractValue(data, i, getName(RuntimeData::Index(i)));
m_gasPtr = m_builder.CreateAlloca(Type::Gas, nullptr, "gas.ptr");
m_builder.CreateStore(m_dataElts[RuntimeData::Index::Gas], m_gasPtr);
llvm::Type* checkStackLimitArgs[] = {Type::Size->getPointerTo(), Type::Size, Type::Size, Type::BytePtr};
m_checkStackLimit = llvm::Function::Create(llvm::FunctionType::get(Type::Void, checkStackLimitArgs, false), llvm::Function::PrivateLinkage, "stack.checkSize", getModule());
m_checkStackLimit->setDoesNotThrow();
@ -180,7 +185,7 @@ llvm::Value* RuntimeManager::getPtr(RuntimeData::Index _index)
llvm::Value* RuntimeManager::get(RuntimeData::Index _index)
{
return getBuilder().CreateLoad(getPtr(_index), getName(_index));
return m_dataElts[_index];
}
void RuntimeManager::set(RuntimeData::Index _index, llvm::Value* _value)
@ -194,8 +199,7 @@ void RuntimeManager::registerReturnData(llvm::Value* _offset, llvm::Value* _size
{
auto memPtr = m_builder.CreateBitCast(getMem(), Type::BytePtr->getPointerTo());
auto mem = getBuilder().CreateLoad(memPtr, "memory");
auto idx = m_builder.CreateTrunc(_offset, Type::Size, "idx"); // Never allow memory index be a type bigger than i64 // TODO: Report bug & fix to LLVM
auto returnDataPtr = getBuilder().CreateGEP(mem, idx);
auto returnDataPtr = getBuilder().CreateGEP(mem, _offset);
set(RuntimeData::ReturnData, returnDataPtr);
auto size64 = getBuilder().CreateTrunc(_size, Type::Size);
@ -212,6 +216,8 @@ void RuntimeManager::exit(ReturnCode _returnCode)
if (m_stack)
m_stack->free();
auto extGasPtr = m_builder.CreateStructGEP(getRuntimeDataType(), getDataPtr(), RuntimeData::Index::Gas, "msg.gas.ptr");
m_builder.CreateStore(getGas(), extGasPtr);
m_builder.CreateRet(Constant::get(_returnCode));
}
@ -265,9 +271,7 @@ llvm::Value* RuntimeManager::getCallDataSize()
llvm::Value* RuntimeManager::getGas()
{
auto gas = get(RuntimeData::Gas);
assert(gas->getType() == Type::Gas);
return gas;
return getBuilder().CreateLoad(getGasPtr(), "gas");
}
llvm::Value* RuntimeManager::getGasPtr()
@ -285,7 +289,7 @@ llvm::Value* RuntimeManager::getMem()
void RuntimeManager::setGas(llvm::Value* _gas)
{
assert(_gas->getType() == Type::Gas);
set(RuntimeData::Gas, _gas);
getBuilder().CreateStore(_gas, getGasPtr());
}
}

6
evmjit/libevmjit/RuntimeManager.h

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

120
evmjit/libevmjit/Stack.cpp

@ -5,7 +5,6 @@
#include "preprocessor/llvm_includes_end.h"
#include "RuntimeManager.h"
#include "Runtime.h"
#include "Utils.h"
#include <set> // DEBUG only
@ -23,96 +22,6 @@ Stack::Stack(llvm::IRBuilder<>& _builder, RuntimeManager& _runtimeManager):
m_stack(_builder, "stack")
{}
llvm::Function* Stack::getPushFunc()
{
auto& func = m_push;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.push", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::WordPtr};
auto extPushFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_push", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto value = rt->getNextNode();
value->setName("value");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(entryBB);
auto a = m_builder.CreateAlloca(Type::Word);
m_builder.CreateStore(value, a);
createCall(extPushFunc, {rt, a});
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Stack::getSetFunc()
{
auto& func = m_set;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::Word};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.set", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size, Type::WordPtr};
auto extSetFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Void, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_set", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto value = index->getNextNode();
value->setName("value");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
m_builder.SetInsertPoint(entryBB);
auto a = m_builder.CreateAlloca(Type::Word);
m_builder.CreateStore(value, a);
createCall(extSetFunc, {rt, index, a});
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Stack::getPopFunc()
{
auto& func = m_pop;
if (!func)
{
llvm::Type* argTypes[] = {Type::RuntimePtr, Type::Size, Type::BytePtr};
func = llvm::Function::Create(llvm::FunctionType::get(Type::Void, argTypes, false), llvm::Function::ExternalLinkage, "stack.pop", getModule());
llvm::Type* extArgTypes[] = {Type::RuntimePtr, Type::Size};
auto extPopFunc = llvm::Function::Create(llvm::FunctionType::get(Type::Bool, extArgTypes, false), llvm::Function::ExternalLinkage, "stack_pop", getModule());
auto rt = &func->getArgumentList().front();
rt->setName("rt");
auto index = rt->getNextNode();
index->setName("index");
auto jmpBuf = index->getNextNode();
jmpBuf->setName("jmpBuf");
InsertPointGuard guard{m_builder};
auto entryBB = llvm::BasicBlock::Create(m_builder.getContext(), {}, func);
auto underflowBB = llvm::BasicBlock::Create(m_builder.getContext(), "Underflow", func);
auto returnBB = llvm::BasicBlock::Create(m_builder.getContext(), "Return", func);
m_builder.SetInsertPoint(entryBB);
auto ok = createCall(extPopFunc, {rt, index});
m_builder.CreateCondBr(ok, returnBB, underflowBB); //TODO: Add branch weight
m_builder.SetInsertPoint(underflowBB);
m_runtimeManager.abort(jmpBuf);
m_builder.CreateUnreachable();
m_builder.SetInsertPoint(returnBB);
m_builder.CreateRetVoid();
}
return func;
}
llvm::Function* Stack::getGetFunc()
{
auto& func = m_get;
@ -175,32 +84,3 @@ void Stack::push(llvm::Value* _value)
}
}
}
extern "C"
{
using namespace dev::eth::jit;
EXPORT void ext_calldataload(RuntimeData* _rtData, i256* _index, byte* o_value)
{
// It asumes all indexes are less than 2^64
auto index = _index->a;
if (_index->b || _index->c || _index->d) // if bigger that 2^64
index = std::numeric_limits<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
evmjit/libevmjit/Stack.h

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

3
evmjit/libevmjit/Type.cpp

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

9
evmjit/libevmjit/Type.h

@ -4,9 +4,9 @@
#include <llvm/IR/Type.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/Metadata.h>
#include "preprocessor/llvm_includes_end.h" // FIXME: LLVM 3.7: check if needed
#include "preprocessor/llvm_includes_end.h"
#include "Common.h"
#include "evmjit/JIT.h" // ReturnCode
namespace dev
{
@ -14,16 +14,13 @@ namespace eth
{
namespace jit
{
using namespace evmjit;
struct Type
{
static llvm::IntegerType* Word;
static llvm::PointerType* WordPtr;
/// Type for doing low precision arithmetics where 256-bit precision is not supported by native target
/// @TODO: Use 64-bit for now. In 128-bit compiler-rt library functions are required
static llvm::IntegerType* lowPrecision;
static llvm::IntegerType* Bool;
static llvm::IntegerType* Size;
static llvm::IntegerType* Gas;

25
evmjit/libevmjit/interface.cpp

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

2
libevm/SmartVM.cpp

@ -47,7 +47,7 @@ bytesConstRef SmartVM::execImpl(u256& io_gas, ExtVMFace& _ext, OnOpFunc const& _
auto vmKind = VMKind::Interpreter; // default VM
// Jitted EVM code already in memory?
if (evmjit::JIT::isCodeReady(eth2llvm(codeHash)))
if (evmjit::JIT::isCodeReady(eth2jit(codeHash)))
{
cnote << "Jitted";
vmKind = VMKind::JIT;

Loading…
Cancel
Save