Browse Source

EVM JIT C++ interface in one header.

cl-refactor
Paweł Bylica 10 years ago
parent
commit
5046dd4bb0
  1. 68
      evmjit/include/evmjit/DataTypes.h
  2. 160
      evmjit/include/evmjit/JIT.h
  3. 1
      evmjit/libevmjit-cpp/Env.cpp
  4. 1
      evmjit/libevmjit-cpp/JitVM.cpp
  5. 2
      evmjit/libevmjit-cpp/JitVM.h
  6. 2
      evmjit/libevmjit-cpp/Utils.h
  7. 1
      evmjit/libevmjit/Arith256.cpp
  8. 6
      evmjit/libevmjit/BasicBlock.h
  9. 6
      evmjit/libevmjit/CMakeLists.txt
  10. 8
      evmjit/libevmjit/Common.h
  11. 1
      evmjit/libevmjit/Compiler.h
  12. 33
      evmjit/libevmjit/ExecutionContext.cpp
  13. 110
      evmjit/libevmjit/ExecutionContext.h
  14. 2
      evmjit/libevmjit/Instruction.h
  15. 25
      evmjit/libevmjit/JIT.cpp
  16. 1
      evmjit/libevmjit/RuntimeManager.h
  17. 2
      evmjit/libevmjit/Type.h
  18. 1
      evmjit/libevmjit/interface.cpp

68
evmjit/include/evmjit/DataTypes.h

@ -1,68 +0,0 @@
#pragma once
#include <cstdint>
#include <functional>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
namespace dev
{
namespace evmjit
{
using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
using code_iterator = byte const*;
struct h256
{
uint64_t words[4];
};
inline bool operator==(h256 _h1, h256 _h2)
{
return _h1.words[0] == _h2.words[0] &&
_h1.words[1] == _h2.words[1] &&
_h1.words[2] == _h2.words[2] &&
_h1.words[3] == _h2.words[3];
}
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t a = 0;
uint64_t b = 0;
uint64_t c = 0;
uint64_t d = 0;
i256() = default;
i256(h256 _h)
{
a = _h.words[0];
b = _h.words[1];
c = _h.words[2];
d = _h.words[3];
}
};
}
}
namespace std
{
template<> struct hash<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]);
};
};
}

160
evmjit/include/evmjit/JIT.h

@ -1,13 +1,153 @@
#pragma once
#include "evmjit/DataTypes.h"
#include <cstdint>
#include <functional>
#ifdef _MSC_VER
#define EXPORT __declspec(dllexport)
#define _ALLOW_KEYWORD_MACROS
#define noexcept throw()
#else
#define EXPORT
#endif
namespace dev
{
namespace evmjit
{
enum class ReturnCode;
class ExecutionContext;
using byte = uint8_t;
using bytes_ref = std::tuple<byte const*, size_t>;
struct h256
{
uint64_t words[4];
};
inline bool operator==(h256 _h1, h256 _h2)
{
return _h1.words[0] == _h2.words[0] &&
_h1.words[1] == _h2.words[1] &&
_h1.words[2] == _h2.words[2] &&
_h1.words[3] == _h2.words[3];
}
/// Representation of 256-bit value binary compatible with LLVM i256
struct i256
{
uint64_t a = 0;
uint64_t b = 0;
uint64_t c = 0;
uint64_t d = 0;
i256() = default;
i256(h256 _h)
{
a = _h.words[0];
b = _h.words[1];
c = _h.words[2];
d = _h.words[3];
}
};
struct RuntimeData
{
enum Index
{
Gas,
GasPrice,
CallData,
CallDataSize,
Address,
Caller,
Origin,
CallValue,
CoinBase,
Difficulty,
GasLimit,
Number,
Timestamp,
Code,
CodeSize,
SuicideDestAddress = Address, ///< Suicide balance destination address
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
};
int64_t gas = 0;
int64_t gasPrice = 0;
byte const* callData = nullptr;
uint64_t callDataSize = 0;
i256 address;
i256 caller;
i256 origin;
i256 callValue;
i256 coinBase;
i256 difficulty;
i256 gasLimit;
uint64_t number = 0;
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
h256 codeHash;
};
/// VM Environment (ExtVM) opaque type
struct Env;
enum class ReturnCode
{
// Success codes
Stop = 0,
Return = 1,
Suicide = 2,
// Standard error codes
OutOfGas = -1,
StackUnderflow = -2,
BadJumpDestination = -3,
BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
// Internal error codes
LLVMConfigError = -101,
LLVMCompileError = -102,
LLVMLinkError = -103,
UnexpectedException = -111,
LinkerWorkaround = -299,
};
class ExecutionContext
{
public:
ExecutionContext() = default;
ExecutionContext(RuntimeData& _data, Env* _env) { init(_data, _env); }
ExecutionContext(ExecutionContext const&) = delete;
ExecutionContext& operator=(ExecutionContext const&) = delete;
EXPORT ~ExecutionContext();
void init(RuntimeData& _data, Env* _env) { m_data = &_data; m_env = _env; }
byte const* code() const { return m_data->code; }
uint64_t codeSize() const { return m_data->codeSize; }
h256 const& codeHash() const { return m_data->codeHash; }
bytes_ref getReturnData() const;
private:
RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract.
Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract.
byte* m_memData = nullptr;
uint64_t m_memSize = 0;
uint64_t m_memCap = 0;
public:
/// Reference to returned data (RETURN opcode used)
bytes_ref returnData;
};
class JIT
{
@ -24,3 +164,17 @@ public:
}
}
namespace std
{
template<> struct hash<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]);
};
};
}

1
evmjit/libevmjit-cpp/Env.cpp

@ -3,7 +3,6 @@
#include <libdevcrypto/SHA3.h>
#include <libevmcore/Params.h>
#include <libevm/ExtVMFace.h>
#include <evmjit/DataTypes.h>
#include "Utils.h"

1
evmjit/libevmjit-cpp/JitVM.cpp

@ -7,7 +7,6 @@
#include <libdevcrypto/SHA3.h>
#include <libevm/VM.h>
#include <libevm/VMFactory.h>
#include <evmjit/JIT.h>
#include "Utils.h"

2
evmjit/libevmjit-cpp/JitVM.h

@ -1,7 +1,7 @@
#pragma once
#include <libevm/VMFace.h>
#include <evmjit/libevmjit/ExecutionContext.h>
#include <evmjit/JIT.h>
namespace dev
{

2
evmjit/libevmjit-cpp/Utils.h

@ -1,6 +1,6 @@
#pragma once
#include <evmjit/DataTypes.h>
#include <evmjit/JIT.h>
namespace dev
{

1
evmjit/libevmjit/Arith256.cpp

@ -8,7 +8,6 @@
#include <llvm/IR/IntrinsicInst.h>
#include "preprocessor/llvm_includes_end.h"
#include "evmjit/DataTypes.h"
#include "Type.h"
#include "Endianness.h"
#include "Utils.h"

6
evmjit/libevmjit/BasicBlock.h

@ -2,8 +2,7 @@
#include <vector>
#include "evmjit/DataTypes.h"
#include "Common.h"
#include "Stack.h"
namespace dev
@ -12,9 +11,8 @@ namespace eth
{
namespace jit
{
using instr_idx = uint64_t;
using namespace evmjit;
using instr_idx = uint64_t;
class BasicBlock
{

6
evmjit/libevmjit/CMakeLists.txt

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

8
evmjit/libevmjit/Common.h

@ -1,18 +1,16 @@
#pragma once
#include <tuple>
#include <cstdint>
namespace dev
{
namespace eth
namespace evmjit
{
namespace jit{
using byte = uint8_t;
using code_iterator = byte const*;
#define UNTESTED assert(false)
}
}
}

1
evmjit/libevmjit/Compiler.h

@ -1,6 +1,5 @@
#pragma once
#include "Common.h"
#include "BasicBlock.h"
namespace dev

33
evmjit/libevmjit/ExecutionContext.cpp

@ -1,33 +0,0 @@
#include "ExecutionContext.h"
#include <cassert>
namespace dev
{
namespace evmjit
{
extern "C" void ext_free(void* _data) noexcept;
ExecutionContext::~ExecutionContext()
{
if (m_memData)
ext_free(m_memData); // Use helper free to check memory leaks
}
bytes_ref ExecutionContext::getReturnData() const
{
auto data = m_data->callData;
auto size = static_cast<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};
}
}
}

110
evmjit/libevmjit/ExecutionContext.h

@ -1,110 +0,0 @@
#pragma once
#include "evmjit/DataTypes.h"
namespace dev
{
namespace evmjit
{
struct RuntimeData
{
enum Index
{
Gas,
GasPrice,
CallData,
CallDataSize,
Address,
Caller,
Origin,
CallValue,
CoinBase,
Difficulty,
GasLimit,
Number,
Timestamp,
Code,
CodeSize,
SuicideDestAddress = Address, ///< Suicide balance destination address
ReturnData = CallData, ///< Return data pointer (set only in case of RETURN)
ReturnDataSize = CallDataSize, ///< Return data size (set only in case of RETURN)
};
int64_t gas = 0;
int64_t gasPrice = 0;
byte const* callData = nullptr;
uint64_t callDataSize = 0;
i256 address;
i256 caller;
i256 origin;
i256 callValue;
i256 coinBase;
i256 difficulty;
i256 gasLimit;
uint64_t number = 0;
int64_t timestamp = 0;
byte const* code = nullptr;
uint64_t codeSize = 0;
h256 codeHash;
};
/// VM Environment (ExtVM) opaque type
struct Env;
enum class ReturnCode
{
// Success codes
Stop = 0,
Return = 1,
Suicide = 2,
// Standard error codes
OutOfGas = -1,
StackUnderflow = -2,
BadJumpDestination = -3,
BadInstruction = -4,
Rejected = -5, ///< Input data (code, gas, block info, etc.) does not meet JIT requirement and execution request has been rejected
// Internal error codes
LLVMConfigError = -101,
LLVMCompileError = -102,
LLVMLinkError = -103,
UnexpectedException = -111,
LinkerWorkaround = -299,
};
class ExecutionContext
{
public:
ExecutionContext() = default;
ExecutionContext(RuntimeData& _data, Env* _env) { init(_data, _env); }
ExecutionContext(ExecutionContext const&) = delete;
ExecutionContext& operator=(ExecutionContext const&) = delete;
EXPORT ~ExecutionContext();
void init(RuntimeData& _data, Env* _env) { m_data = &_data; m_env = _env; }
byte const* code() const { return m_data->code; }
uint64_t codeSize() const { return m_data->codeSize; }
h256 const& codeHash() const { return m_data->codeHash; }
bytes_ref getReturnData() const;
private:
RuntimeData* m_data = nullptr; ///< Pointer to data. Expected by compiled contract.
Env* m_env = nullptr; ///< Pointer to environment proxy. Expected by compiled contract.
byte* m_memData = nullptr;
uint64_t m_memSize = 0;
uint64_t m_memCap = 0;
public:
/// Reference to returned data (RETURN opcode used)
bytes_ref returnData;
};
}
}

2
evmjit/libevmjit/Instruction.h

@ -1,6 +1,6 @@
#pragma once
#include "evmjit/DataTypes.h"
#include "Common.h"
namespace llvm
{

25
evmjit/libevmjit/JIT.cpp

@ -10,7 +10,6 @@
#include <llvm/Support/ManagedStatic.h>
#include "preprocessor/llvm_includes_end.h"
#include "ExecutionContext.h"
#include "Compiler.h"
#include "Optimizer.h"
#include "Cache.h"
@ -211,5 +210,29 @@ ReturnCode JIT::exec(ExecutionContext& _context)
return returnCode;
}
extern "C" void ext_free(void* _data) noexcept;
ExecutionContext::~ExecutionContext()
{
if (m_memData)
ext_free(m_memData); // Use helper free to check memory leaks
}
bytes_ref ExecutionContext::getReturnData() const
{
auto data = m_data->callData;
auto size = static_cast<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};
}
}
}

1
evmjit/libevmjit/RuntimeManager.h

@ -1,6 +1,5 @@
#pragma once
#include "ExecutionContext.h"
#include "CompilerHelper.h"
#include "Type.h"
#include "Instruction.h"

2
evmjit/libevmjit/Type.h

@ -6,7 +6,7 @@
#include <llvm/IR/Metadata.h>
#include "preprocessor/llvm_includes_end.h" // FIXME: LLVM 3.7: check if needed
#include "ExecutionContext.h" // FIXME: crappy dependence
#include "evmjit/JIT.h" // ReturnCode
namespace dev
{

1
evmjit/libevmjit/interface.cpp

@ -1,5 +1,4 @@
#include "evmjit/JIT.h"
#include "ExecutionContext.h"
extern "C"
{

Loading…
Cancel
Save