提交 7ea3f14c 编写于 作者: B Bart Wyatt

move jit into its own isolated space (mostly)

上级 6b14e60f
...@@ -31,10 +31,19 @@ add_library( eosio_chain ...@@ -31,10 +31,19 @@ add_library( eosio_chain
${HEADERS} ${HEADERS}
transaction_metadata.cpp) transaction_metadata.cpp)
target_link_libraries( eosio_chain eos_utilities fc chainbase Logging IR WAST WASM Runtime ) target_link_libraries( eosio_chain eos_utilities fc chainbase Logging IR WAST WASM Runtime
"/usr/local/share/binaryen/lib/libwasm.a"
"/usr/local/share/binaryen/lib/libasmjs.a"
"/usr/local/share/binaryen/lib/libpasses.a"
"/usr/local/share/binaryen/lib/libcfg.a"
"/usr/local/share/binaryen/lib/libast.a"
"/usr/local/share/binaryen/lib/libemscripten-optimizer.a"
"/usr/local/share/binaryen/lib/libsupport.a"
)
target_include_directories( eosio_chain target_include_directories( eosio_chain
PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include" PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" "${CMAKE_CURRENT_BINARY_DIR}/include"
"${CMAKE_CURRENT_SOURCE_DIR}/../wasm-jit/Include" "${CMAKE_CURRENT_SOURCE_DIR}/../wasm-jit/Include" "/usr/local/share/binaryen/src"
) )
if(MSVC) if(MSVC)
......
...@@ -2,14 +2,13 @@ ...@@ -2,14 +2,13 @@
#include <eosio/chain/exceptions.hpp> #include <eosio/chain/exceptions.hpp>
#include <eosio/chain/types.hpp> #include <eosio/chain/types.hpp>
namespace Runtime {
struct MemoryInstance;
}
namespace eosio { namespace chain { namespace eosio { namespace chain {
class apply_context; class apply_context;
class intrinsics_accessor;
namespace webassembly { namespace common {
class intrinsics_accessor;
} }
/** /**
* @class wasm_cache * @class wasm_cache
...@@ -115,7 +114,7 @@ namespace eosio { namespace chain { ...@@ -115,7 +114,7 @@ namespace eosio { namespace chain {
private: private:
wasm_interface(); wasm_interface();
unique_ptr<struct wasm_interface_impl> my; unique_ptr<struct wasm_interface_impl> my;
friend class eosio::chain::intrinsics_accessor; friend class eosio::chain::webassembly::common::intrinsics_accessor;
}; };
} } // eosio::chain } } // eosio::chain
#pragma once #pragma once
#include <eosio/chain/wasm_interface.hpp> #include <eosio/chain/wasm_interface.hpp>
#include <fc/optional.hpp> #include <eosio/chain/webassembly/jit.hpp>
#include "Runtime/Runtime.h"
#include "IR/Types.h"
#include <boost/multiprecision/cpp_dec_float.hpp>
using namespace IR;
using namespace Runtime;
using namespace fc; using namespace fc;
using namespace eosio::chain::webassembly;
namespace eosio { namespace chain { namespace eosio { namespace chain {
using wasm_double = boost::multiprecision::cpp_dec_float_50; struct wasm_cache::entry {
entry(jit::entry&& jit, uint32_t default_sbrk_bytes)
struct wasm_cache::entry { : jit(std::forward<jit::entry>(jit)), default_sbrk_bytes(default_sbrk_bytes)
entry(ModuleInstance *instance, Module *module) {
: instance(instance), module(module) }
{
}
ModuleInstance* instance;
Module* module;
};
struct wasm_context {
wasm_context(wasm_cache::entry &code, apply_context& ctx) : code(code), context(ctx)
{
MemoryInstance* default_mem = Runtime::getDefaultMemory(code.instance);
sbrk_bytes = default_mem ? Runtime::getMemoryNumPages(default_mem) << IR::numBytesPerPageLog2 : 0;
}
wasm_cache::entry& code;
apply_context& context;
uint32_t sbrk_bytes;
};
struct wasm_interface_impl {
optional<wasm_context> current_context;
void call(const string &entry_point, const vector <Value> &args, wasm_cache::entry &code, apply_context &context);
};
class intrinsics_accessor {
public:
static wasm_context &get_context(wasm_interface &wasm) {
FC_ASSERT(wasm.my->current_context.valid());
return *wasm.my->current_context;
}
};
template<typename T>
struct class_from_wasm {
/**
* by default this is just constructing an object
* @param wasm - the wasm_interface to use
* @return
*/
static auto value(wasm_interface &wasm) {
return T(wasm);
}
};
template<>
struct class_from_wasm<apply_context> {
/**
* Don't construct a new apply_context, just return a reference to the existing ont
* @param wasm
* @return
*/
static auto &value(wasm_interface &wasm) {
return intrinsics_accessor::get_context(wasm).context;
}
};
/**
* class to represent an in-wasm-memory array
* it is a hint to the transcriber that the next parameter will
* be a size (data bytes length) and that the pair are validated together
* This triggers the template specialization of intrinsic_invoker_impl
* @tparam T
*/
template<typename T>
struct array_ptr {
explicit array_ptr (wasm_interface& wasm, U32 ptr, size_t length)
: value(validated_ptr(wasm, ptr, length)){
}
static T* validated_ptr (wasm_interface& wasm, U32 ptr, size_t length) {
auto mem = getDefaultMemory(intrinsics_accessor::get_context(wasm).code.instance);
if(!mem || ptr + length > IR::numBytesPerPage*Runtime::getMemoryNumPages(mem))
Runtime::causeException(Exception::Cause::accessViolation);
return (T*)(getMemoryBaseAddress(mem) + ptr);
}
typename std::add_lvalue_reference<T>::type operator*() const {
return *value;
}
T *operator->() const noexcept {
return value;
}
template<typename U>
operator U *() const {
return static_cast<U *>(value);
}
T *value;
};
/**
* class to represent an in-wasm-memory char array that must be null terminated
*/
struct null_terminated_ptr {
explicit null_terminated_ptr(wasm_interface& wasm, U32 ptr) {
auto mem = getDefaultMemory(intrinsics_accessor::get_context(wasm).code.instance);
if(!mem)
Runtime::causeException(Exception::Cause::accessViolation);
value = (char*)(getMemoryBaseAddress(mem) + ptr);
const char* p = value;
const char* const top_of_memory = (char*)(getMemoryBaseAddress(mem) + IR::numBytesPerPage*Runtime::getMemoryNumPages(mem));
while(p < top_of_memory)
if(*p++ == '\0')
return;
Runtime::causeException(Exception::Cause::accessViolation);
}
typename std::add_lvalue_reference<char>::type operator*() const {
return *value;
}
char *operator->() const noexcept {
return value;
}
template<typename U>
operator U *() const {
return static_cast<U *>(value);
}
char *value;
};
/**
* template that maps native types to WASM VM types
* @tparam T the native type
*/
template<typename T>
struct native_to_wasm {
using type = void;
};
/**
* specialization for mapping pointers to int32's
*/
template<typename T>
struct native_to_wasm<T *> {
using type = I32;
};
/**
* Mappings for native types
*/
template<>
struct native_to_wasm<int32_t> {
using type = I32;
};
template<>
struct native_to_wasm<uint32_t> {
using type = I32;
};
template<>
struct native_to_wasm<int64_t> {
using type = I64;
};
template<>
struct native_to_wasm<uint64_t> {
using type = I64;
};
template<>
struct native_to_wasm<bool> {
using type = I32;
};
template<>
struct native_to_wasm<const name &> {
using type = I64;
};
template<>
struct native_to_wasm<name> {
using type = I64;
};
template<>
struct native_to_wasm<wasm_double> {
using type = I64;
};
template<>
struct native_to_wasm<const fc::time_point_sec &> {
using type = I32;
};
template<>
struct native_to_wasm<fc::time_point_sec> {
using type = I32;
};
// convenience alias
template<typename T>
using native_to_wasm_t = typename native_to_wasm<T>::type;
template<typename T>
auto convert_native_to_wasm(const wasm_interface &wasm, T val) {
return native_to_wasm_t<T>(val);
}
auto convert_native_to_wasm(const wasm_interface &wasm, const name &val) {
return native_to_wasm_t<const name &>(val.value);
}
auto convert_native_to_wasm(const wasm_interface &wasm, const fc::time_point_sec &val) {
return native_to_wasm_t<const fc::time_point_sec &>(val.sec_since_epoch());
}
auto convert_native_to_wasm(wasm_interface &wasm, char* ptr) {
auto mem = getDefaultMemory(intrinsics_accessor::get_context(wasm).code.instance);
if(!mem)
Runtime::causeException(Exception::Cause::accessViolation);
char* base = (char*)getMemoryBaseAddress(mem);
char* top_of_memory = base + IR::numBytesPerPage*Runtime::getMemoryNumPages(mem);
if(ptr < base || ptr >= top_of_memory)
Runtime::causeException(Exception::Cause::accessViolation);
return (int)(ptr - base);
}
template<typename T>
auto convert_wasm_to_native(native_to_wasm_t<T> val) {
return T(val);
}
template<>
auto convert_wasm_to_native<wasm_double>(I64 val) {
return wasm_double(*reinterpret_cast<wasm_double *>(&val));
}
template<typename T>
struct wasm_to_value_type;
template<>
struct wasm_to_value_type<I32> {
static constexpr auto value = ValueType::i32;
};
template<>
struct wasm_to_value_type<I64> {
static constexpr auto value = ValueType::i64;
};
template<typename T>
constexpr auto wasm_to_value_type_v = wasm_to_value_type<T>::value;
template<typename T>
struct wasm_to_rvalue_type;
template<>
struct wasm_to_rvalue_type<I32> {
static constexpr auto value = ResultType::i32;
};
template<>
struct wasm_to_rvalue_type<I64> {
static constexpr auto value = ResultType::i64;
};
template<>
struct wasm_to_rvalue_type<void> {
static constexpr auto value = ResultType::none;
};
template<>
struct wasm_to_rvalue_type<const name&> {
static constexpr auto value = ResultType::i64;
};
template<>
struct wasm_to_rvalue_type<name> {
static constexpr auto value = ResultType::i64;
};
template<>
struct wasm_to_rvalue_type<char*> {
static constexpr auto value = ResultType::i32;
};
template<>
struct wasm_to_rvalue_type<fc::time_point_sec> {
static constexpr auto value = ResultType::i32;
};
template<typename T>
constexpr auto wasm_to_rvalue_type_v = wasm_to_rvalue_type<T>::value;
template<typename T>
struct is_reference_from_value {
static constexpr bool value = false;
};
template<>
struct is_reference_from_value<name> {
static constexpr bool value = true;
};
template<>
struct is_reference_from_value<fc::time_point_sec> {
static constexpr bool value = true;
};
template<typename T>
constexpr bool is_reference_from_value_v = is_reference_from_value<T>::value;
struct void_type {
};
/**
* Forward declaration of provider for FunctionType given a desired C ABI signature
*/
template<typename>
struct wasm_function_type_provider;
/** jit::entry jit;
* specialization to destructure return and arguments uint32_t default_sbrk_bytes;
*/
template<typename Ret, typename ...Args>
struct wasm_function_type_provider<Ret(Args...)> {
static const FunctionType *type() {
return FunctionType::get(wasm_to_rvalue_type_v<Ret>, {wasm_to_value_type_v<Args> ...});
}
};
/**
* Forward declaration of the invoker type which transcribes arguments to/from a native method
* and injects the appropriate checks
*
* @tparam Ret - the return type of the native function
* @tparam NativeParameters - a std::tuple of the remaining native parameters to transcribe
* @tparam WasmParameters - a std::tuple of the transribed parameters
*/
template<typename Ret, typename NativeParameters, typename WasmParameters>
struct intrinsic_invoker_impl;
/**
* Specialization for the fully transcribed signature
* @tparam Ret - the return type of the native function
* @tparam Translated - the arguments to the wasm function
*/
template<typename Ret, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<>, std::tuple<Translated...>> {
using next_method_type = Ret (*)(wasm_interface &, Translated...);
template<next_method_type Method>
static native_to_wasm_t<Ret> invoke(Translated... translated) {
wasm_interface &wasm = wasm_interface::get();
return convert_native_to_wasm(wasm, Method(wasm, translated...));
}
template<next_method_type Method>
static const auto fn() {
return invoke<Method>;
}
};
/**
* specialization of the fully transcribed signature for void return values
* @tparam Translated - the arguments to the wasm function
*/
template<typename ...Translated>
struct intrinsic_invoker_impl<void_type, std::tuple<>, std::tuple<Translated...>> {
using next_method_type = void_type (*)(wasm_interface &, Translated...);
template<next_method_type Method>
static void invoke(Translated... translated) {
wasm_interface &wasm = wasm_interface::get();
Method(wasm, translated...);
}
template<next_method_type Method>
static const auto fn() {
return invoke<Method>;
}
};
/**
* Sepcialization for transcribing a simple type in the native method signature
* @tparam Ret - the return type of the native method
* @tparam Input - the type of the native parameter to transcribe
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename Ret, typename Input, typename... Inputs, typename... Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<Input, Inputs...>, std::tuple<Translated...>> {
using translated_type = native_to_wasm_t<Input>;
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., translated_type>>;
using then_type = Ret (*)(wasm_interface &, Input, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, translated_type last) {
auto native = convert_wasm_to_native<Input>(last);
return Then(wasm, native, rest..., translated...);
};
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a array_ptr type in the native method signature
* This type transcribes into 2 wasm parameters: a pointer and byte length and checks the validity of that memory
* range before dispatching to the native method
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename T, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<array_ptr<T>, size_t, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32, I32>>;
using then_type = Ret(*)(wasm_interface &, array_ptr<T>, size_t, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr, I32 size) {
const auto length = size_t(size);
return Then(wasm, array_ptr<T>(wasm, ptr, length), length, rest..., translated...);
}; };
template<then_type Then> struct wasm_interface_impl {
static const auto fn() { optional<common::wasm_context> current_context;
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a null_terminated_ptr type in the native method signature
* This type transcribes 1 wasm parameters: a char pointer which is validated to contain
* a null value before the end of the allocated memory.
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<null_terminated_ptr, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32>>;
using then_type = Ret(*)(wasm_interface &, null_terminated_ptr, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr) {
return Then(wasm, null_terminated_ptr(wasm, ptr), rest..., translated...);
};
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a pair of array_ptr types in the native method signature that share size
* This type transcribes into 3 wasm parameters: 2 pointers and byte length and checks the validity of those memory
* ranges before dispatching to the native method
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename T, typename U, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<array_ptr<T>, array_ptr<U>, size_t, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32, I32, I32>>;
using then_type = Ret(*)(wasm_interface &, array_ptr<T>, array_ptr<U>, size_t, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr_t, I32 ptr_u, I32 size) {
const auto length = size_t(size);
return Then(wasm, array_ptr<T>(wasm, ptr_t, length), array_ptr<U>(wasm, ptr_u, length), length, rest..., translated...);
};
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing memset parameters
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename Ret>
struct intrinsic_invoker_impl<Ret, std::tuple<array_ptr<char>, int, size_t>, std::tuple<>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<>, std::tuple<I32, I32, I32>>;
using then_type = Ret(*)(wasm_interface &, array_ptr<char>, int, size_t);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, I32 ptr, I32 value, I32 size) {
const auto length = size_t(size);
return Then(wasm, array_ptr<char>(wasm, ptr, length), value, length);
};
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a pointer type in the native method signature
* This type transcribes into an int32 pointer checks the validity of that memory
* range before dispatching to the native method
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename T, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<T *, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32>>;
using then_type = Ret (*)(wasm_interface &, T *, Inputs..., Translated...);
template<then_type Then> void call(const string &entry_point, const vector <Value> &args, wasm_cache::entry &code, apply_context &context);
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr) {
T* base = array_ptr<T>::validated_ptr(wasm, ptr, sizeof(T));
return Then(wasm, base, rest..., translated...);
}; };
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a reference to a name which can be passed as a native value
* This type transcribes into a native type which is loaded by value into a
* variable on the stack and then passed by reference to the intrinsic.
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<const name&, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., native_to_wasm_t<const name&> >>;
using then_type = Ret (*)(wasm_interface &, const name&, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, native_to_wasm_t<const name&> wasm_value) {
auto value = name(wasm_value);
return Then(wasm, value, rest..., translated...);
}
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a reference to a fc::time_point_sec which can be passed as a native value
* This type transcribes into a native type which is loaded by value into a
* variable on the stack and then passed by reference to the intrinsic.
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<const fc::time_point_sec&, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., native_to_wasm_t<const fc::time_point_sec&> >>;
using then_type = Ret (*)(wasm_interface &, const fc::time_point_sec&, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, native_to_wasm_t<const fc::time_point_sec&> wasm_value) {
auto value = fc::time_point_sec(wasm_value);
return Then(wasm, value, rest..., translated...);
}
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a reference type in the native method signature
* This type transcribes into an int32 pointer checks the validity of that memory
* range before dispatching to the native method
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename T, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<T &, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32>>;
using then_type = Ret (*)(wasm_interface &, T &, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr) {
// references cannot be created for null pointers
FC_ASSERT(ptr != 0);
auto mem = getDefaultMemory(intrinsics_accessor::get_context(wasm).code.instance);
if(!mem || ptr+sizeof(T) >= IR::numBytesPerPage*Runtime::getMemoryNumPages(mem))
Runtime::causeException(Exception::Cause::accessViolation);
T &base = *(T*)(getMemoryBaseAddress(mem)+ptr);
return Then(wasm, base, rest..., translated...);
}
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* forward declaration of a wrapper class to call methods of the class
*/
template<typename WasmSig, typename Ret, typename MethodSig, typename Cls, typename... Params>
struct intrinsic_function_invoker {
using impl = intrinsic_invoker_impl<Ret, std::tuple<Params...>, std::tuple<>>;
template<MethodSig Method>
static Ret wrapper(wasm_interface &wasm, Params... params) {
return (class_from_wasm<Cls>::value(wasm).*Method)(params...);
}
template<MethodSig Method>
static const WasmSig *fn() {
auto fn = impl::template fn<wrapper<Method>>();
static_assert(std::is_same<WasmSig *, decltype(fn)>::value,
"Intrinsic function signature does not match the ABI");
return fn;
}
};
template<typename WasmSig, typename MethodSig, typename Cls, typename... Params>
struct intrinsic_function_invoker<WasmSig, void, MethodSig, Cls, Params...> {
using impl = intrinsic_invoker_impl<void_type, std::tuple<Params...>, std::tuple<>>;
template<MethodSig Method>
static void_type wrapper(wasm_interface &wasm, Params... params) {
(class_from_wasm<Cls>::value(wasm).*Method)(params...);
return void_type();
}
template<MethodSig Method>
static const WasmSig *fn() {
auto fn = impl::template fn<wrapper<Method>>();
static_assert(std::is_same<WasmSig *, decltype(fn)>::value,
"Intrinsic function signature does not match the ABI");
return fn;
}
};
template<typename, typename>
struct intrinsic_function_invoker_wrapper;
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...)> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...), Cls, Params...>;
};
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) const> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...) const, Cls, Params...>;
};
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) volatile> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...) volatile, Cls, Params...>;
};
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) const volatile> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...) const volatile, Cls, Params...>;
};
#define _ADD_PAREN_1(...) ((__VA_ARGS__)) _ADD_PAREN_2
#define _ADD_PAREN_2(...) ((__VA_ARGS__)) _ADD_PAREN_1
#define _ADD_PAREN_1_END
#define _ADD_PAREN_2_END
#define _WRAPPED_SEQ(SEQ) BOOST_PP_CAT(_ADD_PAREN_1 SEQ, _END)
#define __INTRINSIC_NAME(LABEL, SUFFIX) LABEL##SUFFIX
#define _INTRINSIC_NAME(LABEL, SUFFIX) __INTRINSIC_NAME(LABEL,SUFFIX)
#define _REGISTER_INTRINSIC_EXPLICIT(CLS, METHOD, WASM_SIG, NAME, SIG)\ #define _REGISTER_INTRINSIC_EXPLICIT(CLS, METHOD, WASM_SIG, NAME, SIG)\
static Intrinsics::Function _INTRINSIC_NAME(__intrinsic_fn, __COUNTER__) (\ _REGISTER_JIT_INTRINSIC(CLS, METHOD, WASM_SIG, NAME, SIG)
"env." NAME,\
eosio::chain::wasm_function_type_provider<WASM_SIG>::type(),\
(void *)eosio::chain::intrinsic_function_invoker_wrapper<WASM_SIG, SIG>::type::fn<&CLS::METHOD>()\
);\
#define _REGISTER_INTRINSIC4(CLS, METHOD, WASM_SIG, NAME, SIG)\ #define _REGISTER_INTRINSIC4(CLS, METHOD, WASM_SIG, NAME, SIG)\
_REGISTER_INTRINSIC_EXPLICIT(CLS, METHOD, WASM_SIG, NAME, SIG ) _REGISTER_INTRINSIC_EXPLICIT(CLS, METHOD, WASM_SIG, NAME, SIG )
......
#pragma once
#include <eosio/chain/wasm_interface.hpp>
#include <boost/multiprecision/cpp_dec_float.hpp>
using namespace fc;
namespace eosio { namespace chain { namespace webassembly { namespace common {
using wasm_double = boost::multiprecision::cpp_dec_float_50;
struct wasm_context {
wasm_context(wasm_cache::entry &code, apply_context& ctx, uint32_t sbrk_bytes) : code(code), context(ctx)
{
}
eosio::chain::wasm_cache::entry& code;
apply_context& context;
uint32_t sbrk_bytes;
};
class intrinsics_accessor {
public:
static wasm_context &get_context(wasm_interface &wasm);
};
template<typename T>
struct class_from_wasm {
/**
* by default this is just constructing an object
* @param wasm - the wasm_interface to use
* @return
*/
static auto value(wasm_interface &wasm) {
return T(wasm);
}
};
template<>
struct class_from_wasm<apply_context> {
/**
* Don't construct a new apply_context, just return a reference to the existing ont
* @param wasm
* @return
*/
static auto &value(wasm_interface &wasm) {
return intrinsics_accessor::get_context(wasm).context;
}
};
/**
* class to represent an in-wasm-memory array
* it is a hint to the transcriber that the next parameter will
* be a size (data bytes length) and that the pair are validated together
* This triggers the template specialization of intrinsic_invoker_impl
* @tparam T
*/
template<typename T>
struct array_ptr {
explicit array_ptr (T * value) : value(value) {}
typename std::add_lvalue_reference<T>::type operator*() const {
return *value;
}
T *operator->() const noexcept {
return value;
}
template<typename U>
operator U *() const {
return static_cast<U *>(value);
}
T *value;
};
/**
* class to represent an in-wasm-memory char array that must be null terminated
*/
struct null_terminated_ptr {
explicit null_terminated_ptr(char* value) : value(value) {}
typename std::add_lvalue_reference<char>::type operator*() const {
return *value;
}
char *operator->() const noexcept {
return value;
}
template<typename U>
operator U *() const {
return static_cast<U *>(value);
}
char *value;
};
} } } } // eosio::chain
#pragma once
#include <eosio/chain/webassembly/common.hpp>
#include "Runtime/Runtime.h"
#include "IR/Types.h"
using namespace IR;
using namespace Runtime;
using namespace fc;
using namespace eosio::chain::webassembly::common;
namespace eosio { namespace chain { namespace webassembly { namespace jit {
struct entry {
entry(ModuleInstance *instance, Module *module)
: instance(instance), module(module)
{
}
ModuleInstance* instance;
Module* module;
static const entry& get(wasm_interface& wasm);
};
/**
* class to represent an in-wasm-memory array
* it is a hint to the transcriber that the next parameter will
* be a size (data bytes length) and that the pair are validated together
* This triggers the template specialization of intrinsic_invoker_impl
* @tparam T
*/
template<typename T>
array_ptr<T> array_ptr_impl (wasm_interface& wasm, U32 ptr, size_t length)
{
auto mem = getDefaultMemory(entry::get(wasm).instance);
if(!mem || ptr + length > IR::numBytesPerPage*Runtime::getMemoryNumPages(mem))
Runtime::causeException(Exception::Cause::accessViolation);
return array_ptr<T>((T*)(getMemoryBaseAddress(mem) + ptr));
}
/**
* class to represent an in-wasm-memory char array that must be null terminated
*/
null_terminated_ptr null_terminated_ptr_impl(wasm_interface& wasm, U32 ptr)
{
auto mem = getDefaultMemory(entry::get(wasm).instance);
if(!mem)
Runtime::causeException(Exception::Cause::accessViolation);
char *value = (char*)(getMemoryBaseAddress(mem) + ptr);
const char* p = value;
const char* const top_of_memory = (char*)(getMemoryBaseAddress(mem) + IR::numBytesPerPage*Runtime::getMemoryNumPages(mem));
while(p < top_of_memory)
if(*p++ == '\0')
return null_terminated_ptr(value);
Runtime::causeException(Exception::Cause::accessViolation);
}
/**
* template that maps native types to WASM VM types
* @tparam T the native type
*/
template<typename T>
struct native_to_wasm {
using type = void;
};
/**
* specialization for mapping pointers to int32's
*/
template<typename T>
struct native_to_wasm<T *> {
using type = I32;
};
/**
* Mappings for native types
*/
template<>
struct native_to_wasm<int32_t> {
using type = I32;
};
template<>
struct native_to_wasm<uint32_t> {
using type = I32;
};
template<>
struct native_to_wasm<int64_t> {
using type = I64;
};
template<>
struct native_to_wasm<uint64_t> {
using type = I64;
};
template<>
struct native_to_wasm<bool> {
using type = I32;
};
template<>
struct native_to_wasm<const name &> {
using type = I64;
};
template<>
struct native_to_wasm<name> {
using type = I64;
};
template<>
struct native_to_wasm<wasm_double> {
using type = I64;
};
template<>
struct native_to_wasm<const fc::time_point_sec &> {
using type = I32;
};
template<>
struct native_to_wasm<fc::time_point_sec> {
using type = I32;
};
// convenience alias
template<typename T>
using native_to_wasm_t = typename native_to_wasm<T>::type;
template<typename T>
auto convert_native_to_wasm(const wasm_interface &wasm, T val) {
return native_to_wasm_t<T>(val);
}
auto convert_native_to_wasm(const wasm_interface &wasm, const name &val) {
return native_to_wasm_t<const name &>(val.value);
}
auto convert_native_to_wasm(const wasm_interface &wasm, const fc::time_point_sec &val) {
return native_to_wasm_t<const fc::time_point_sec &>(val.sec_since_epoch());
}
auto convert_native_to_wasm(wasm_interface &wasm, char* ptr) {
auto mem = getDefaultMemory(entry::get(wasm).instance);
if(!mem)
Runtime::causeException(Exception::Cause::accessViolation);
char* base = (char*)getMemoryBaseAddress(mem);
char* top_of_memory = base + IR::numBytesPerPage*Runtime::getMemoryNumPages(mem);
if(ptr < base || ptr >= top_of_memory)
Runtime::causeException(Exception::Cause::accessViolation);
return (int)(ptr - base);
}
template<typename T>
auto convert_wasm_to_native(native_to_wasm_t<T> val) {
return T(val);
}
template<>
auto convert_wasm_to_native<wasm_double>(I64 val) {
return wasm_double(*reinterpret_cast<wasm_double *>(&val));
}
template<typename T>
struct wasm_to_value_type;
template<>
struct wasm_to_value_type<I32> {
static constexpr auto value = ValueType::i32;
};
template<>
struct wasm_to_value_type<I64> {
static constexpr auto value = ValueType::i64;
};
template<typename T>
constexpr auto wasm_to_value_type_v = wasm_to_value_type<T>::value;
template<typename T>
struct wasm_to_rvalue_type;
template<>
struct wasm_to_rvalue_type<I32> {
static constexpr auto value = ResultType::i32;
};
template<>
struct wasm_to_rvalue_type<I64> {
static constexpr auto value = ResultType::i64;
};
template<>
struct wasm_to_rvalue_type<void> {
static constexpr auto value = ResultType::none;
};
template<>
struct wasm_to_rvalue_type<const name&> {
static constexpr auto value = ResultType::i64;
};
template<>
struct wasm_to_rvalue_type<name> {
static constexpr auto value = ResultType::i64;
};
template<>
struct wasm_to_rvalue_type<char*> {
static constexpr auto value = ResultType::i32;
};
template<>
struct wasm_to_rvalue_type<fc::time_point_sec> {
static constexpr auto value = ResultType::i32;
};
template<typename T>
constexpr auto wasm_to_rvalue_type_v = wasm_to_rvalue_type<T>::value;
template<typename T>
struct is_reference_from_value {
static constexpr bool value = false;
};
template<>
struct is_reference_from_value<name> {
static constexpr bool value = true;
};
template<>
struct is_reference_from_value<fc::time_point_sec> {
static constexpr bool value = true;
};
template<typename T>
constexpr bool is_reference_from_value_v = is_reference_from_value<T>::value;
struct void_type {
};
/**
* Forward declaration of provider for FunctionType given a desired C ABI signature
*/
template<typename>
struct wasm_function_type_provider;
/**
* specialization to destructure return and arguments
*/
template<typename Ret, typename ...Args>
struct wasm_function_type_provider<Ret(Args...)> {
static const FunctionType *type() {
return FunctionType::get(wasm_to_rvalue_type_v<Ret>, {wasm_to_value_type_v<Args> ...});
}
};
/**
* Forward declaration of the invoker type which transcribes arguments to/from a native method
* and injects the appropriate checks
*
* @tparam Ret - the return type of the native function
* @tparam NativeParameters - a std::tuple of the remaining native parameters to transcribe
* @tparam WasmParameters - a std::tuple of the transribed parameters
*/
template<typename Ret, typename NativeParameters, typename WasmParameters>
struct intrinsic_invoker_impl;
/**
* Specialization for the fully transcribed signature
* @tparam Ret - the return type of the native function
* @tparam Translated - the arguments to the wasm function
*/
template<typename Ret, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<>, std::tuple<Translated...>> {
using next_method_type = Ret (*)(wasm_interface &, Translated...);
template<next_method_type Method>
static native_to_wasm_t<Ret> invoke(Translated... translated) {
wasm_interface &wasm = wasm_interface::get();
return convert_native_to_wasm(wasm, Method(wasm, translated...));
}
template<next_method_type Method>
static const auto fn() {
return invoke<Method>;
}
};
/**
* specialization of the fully transcribed signature for void return values
* @tparam Translated - the arguments to the wasm function
*/
template<typename ...Translated>
struct intrinsic_invoker_impl<void_type, std::tuple<>, std::tuple<Translated...>> {
using next_method_type = void_type (*)(wasm_interface &, Translated...);
template<next_method_type Method>
static void invoke(Translated... translated) {
wasm_interface &wasm = wasm_interface::get();
Method(wasm, translated...);
}
template<next_method_type Method>
static const auto fn() {
return invoke<Method>;
}
};
/**
* Sepcialization for transcribing a simple type in the native method signature
* @tparam Ret - the return type of the native method
* @tparam Input - the type of the native parameter to transcribe
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename Ret, typename Input, typename... Inputs, typename... Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<Input, Inputs...>, std::tuple<Translated...>> {
using translated_type = native_to_wasm_t<Input>;
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., translated_type>>;
using then_type = Ret (*)(wasm_interface &, Input, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, translated_type last) {
auto native = convert_wasm_to_native<Input>(last);
return Then(wasm, native, rest..., translated...);
};
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a array_ptr type in the native method signature
* This type transcribes into 2 wasm parameters: a pointer and byte length and checks the validity of that memory
* range before dispatching to the native method
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename T, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<array_ptr<T>, size_t, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32, I32>>;
using then_type = Ret(*)(wasm_interface &, array_ptr<T>, size_t, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr, I32 size) {
const auto length = size_t(size);
return Then(wasm, array_ptr_impl<T>(wasm, ptr, length), length, rest..., translated...);
};
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a null_terminated_ptr type in the native method signature
* This type transcribes 1 wasm parameters: a char pointer which is validated to contain
* a null value before the end of the allocated memory.
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<null_terminated_ptr, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32>>;
using then_type = Ret(*)(wasm_interface &, null_terminated_ptr, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr) {
return Then(wasm, null_terminated_ptr_impl(wasm, ptr), rest..., translated...);
};
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a pair of array_ptr types in the native method signature that share size
* This type transcribes into 3 wasm parameters: 2 pointers and byte length and checks the validity of those memory
* ranges before dispatching to the native method
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename T, typename U, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<array_ptr<T>, array_ptr<U>, size_t, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32, I32, I32>>;
using then_type = Ret(*)(wasm_interface &, array_ptr<T>, array_ptr<U>, size_t, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr_t, I32 ptr_u, I32 size) {
const auto length = size_t(size);
return Then(wasm, array_ptr_impl<T>(wasm, ptr_t, length), array_ptr_impl<U>(wasm, ptr_u, length), length, rest..., translated...);
};
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing memset parameters
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename Ret>
struct intrinsic_invoker_impl<Ret, std::tuple<array_ptr<char>, int, size_t>, std::tuple<>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<>, std::tuple<I32, I32, I32>>;
using then_type = Ret(*)(wasm_interface &, array_ptr<char>, int, size_t);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, I32 ptr, I32 value, I32 size) {
const auto length = size_t(size);
return Then(wasm, array_ptr_impl<char>(wasm, ptr, length), value, length);
};
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a pointer type in the native method signature
* This type transcribes into an int32 pointer checks the validity of that memory
* range before dispatching to the native method
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename T, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<T *, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32>>;
using then_type = Ret (*)(wasm_interface &, T *, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr) {
T* base = array_ptr_impl<T>(wasm, ptr, sizeof(T));
return Then(wasm, base, rest..., translated...);
};
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a reference to a name which can be passed as a native value
* This type transcribes into a native type which is loaded by value into a
* variable on the stack and then passed by reference to the intrinsic.
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<const name&, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., native_to_wasm_t<const name&> >>;
using then_type = Ret (*)(wasm_interface &, const name&, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, native_to_wasm_t<const name&> wasm_value) {
auto value = name(wasm_value);
return Then(wasm, value, rest..., translated...);
}
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a reference to a fc::time_point_sec which can be passed as a native value
* This type transcribes into a native type which is loaded by value into a
* variable on the stack and then passed by reference to the intrinsic.
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<const fc::time_point_sec&, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., native_to_wasm_t<const fc::time_point_sec&> >>;
using then_type = Ret (*)(wasm_interface &, const fc::time_point_sec&, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, native_to_wasm_t<const fc::time_point_sec&> wasm_value) {
auto value = fc::time_point_sec(wasm_value);
return Then(wasm, value, rest..., translated...);
}
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* Specialization for transcribing a reference type in the native method signature
* This type transcribes into an int32 pointer checks the validity of that memory
* range before dispatching to the native method
*
* @tparam Ret - the return type of the native method
* @tparam Inputs - the remaining native parameters to transcribe
* @tparam Translated - the list of transcribed wasm parameters
*/
template<typename T, typename Ret, typename... Inputs, typename ...Translated>
struct intrinsic_invoker_impl<Ret, std::tuple<T &, Inputs...>, std::tuple<Translated...>> {
using next_step = intrinsic_invoker_impl<Ret, std::tuple<Inputs...>, std::tuple<Translated..., I32>>;
using then_type = Ret (*)(wasm_interface &, T &, Inputs..., Translated...);
template<then_type Then>
static Ret translate_one(wasm_interface &wasm, Inputs... rest, Translated... translated, I32 ptr) {
// references cannot be created for null pointers
FC_ASSERT(ptr != 0);
auto mem = getDefaultMemory(entry::get(wasm).instance);
if(!mem || ptr+sizeof(T) >= IR::numBytesPerPage*Runtime::getMemoryNumPages(mem))
Runtime::causeException(Exception::Cause::accessViolation);
T &base = *(T*)(getMemoryBaseAddress(mem)+ptr);
return Then(wasm, base, rest..., translated...);
}
template<then_type Then>
static const auto fn() {
return next_step::template fn<translate_one<Then>>();
}
};
/**
* forward declaration of a wrapper class to call methods of the class
*/
template<typename WasmSig, typename Ret, typename MethodSig, typename Cls, typename... Params>
struct intrinsic_function_invoker {
using impl = intrinsic_invoker_impl<Ret, std::tuple<Params...>, std::tuple<>>;
template<MethodSig Method>
static Ret wrapper(wasm_interface &wasm, Params... params) {
return (class_from_wasm<Cls>::value(wasm).*Method)(params...);
}
template<MethodSig Method>
static const WasmSig *fn() {
auto fn = impl::template fn<wrapper<Method>>();
static_assert(std::is_same<WasmSig *, decltype(fn)>::value,
"Intrinsic function signature does not match the ABI");
return fn;
}
};
template<typename WasmSig, typename MethodSig, typename Cls, typename... Params>
struct intrinsic_function_invoker<WasmSig, void, MethodSig, Cls, Params...> {
using impl = intrinsic_invoker_impl<void_type, std::tuple<Params...>, std::tuple<>>;
template<MethodSig Method>
static void_type wrapper(wasm_interface &wasm, Params... params) {
(class_from_wasm<Cls>::value(wasm).*Method)(params...);
return void_type();
}
template<MethodSig Method>
static const WasmSig *fn() {
auto fn = impl::template fn<wrapper<Method>>();
static_assert(std::is_same<WasmSig *, decltype(fn)>::value,
"Intrinsic function signature does not match the ABI");
return fn;
}
};
template<typename, typename>
struct intrinsic_function_invoker_wrapper;
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...)> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...), Cls, Params...>;
};
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) const> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...) const, Cls, Params...>;
};
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) volatile> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...) volatile, Cls, Params...>;
};
template<typename WasmSig, typename Cls, typename Ret, typename... Params>
struct intrinsic_function_invoker_wrapper<WasmSig, Ret (Cls::*)(Params...) const volatile> {
using type = intrinsic_function_invoker<WasmSig, Ret, Ret (Cls::*)(Params...) const volatile, Cls, Params...>;
};
#define _ADD_PAREN_1(...) ((__VA_ARGS__)) _ADD_PAREN_2
#define _ADD_PAREN_2(...) ((__VA_ARGS__)) _ADD_PAREN_1
#define _ADD_PAREN_1_END
#define _ADD_PAREN_2_END
#define _WRAPPED_SEQ(SEQ) BOOST_PP_CAT(_ADD_PAREN_1 SEQ, _END)
#define __INTRINSIC_NAME(LABEL, SUFFIX) LABEL##SUFFIX
#define _INTRINSIC_NAME(LABEL, SUFFIX) __INTRINSIC_NAME(LABEL,SUFFIX)
#define _REGISTER_JIT_INTRINSIC(CLS, METHOD, WASM_SIG, NAME, SIG)\
static Intrinsics::Function _INTRINSIC_NAME(__intrinsic_fn, __COUNTER__) (\
"env." NAME,\
eosio::chain::webassembly::jit::wasm_function_type_provider<WASM_SIG>::type(),\
(void *)eosio::chain::webassembly::jit::intrinsic_function_invoker_wrapper<WASM_SIG, SIG>::type::fn<&CLS::METHOD>()\
);\
} } } }// eosio::chain::webassembly::jit
...@@ -69,6 +69,8 @@ using boost::asio::io_service; ...@@ -69,6 +69,8 @@ using boost::asio::io_service;
namespace eosio { namespace chain { namespace eosio { namespace chain {
using namespace contracts; using namespace contracts;
using namespace webassembly;
using namespace webassembly::common;
/** /**
* Integration with the WASM Linker to resolve our intrinsics * Integration with the WASM Linker to resolve our intrinsics
...@@ -251,7 +253,10 @@ namespace eosio { namespace chain { ...@@ -251,7 +253,10 @@ namespace eosio { namespace chain {
// find or create a new entry // find or create a new entry
auto iter = _cache.emplace(code_id, code_info(mem_end, std::move(mem_image))).first; auto iter = _cache.emplace(code_id, code_info(mem_end, std::move(mem_image))).first;
iter->second.instances.emplace_back(std::make_unique<wasm_cache::entry>(instance, module)); MemoryInstance* default_mem = Runtime::getDefaultMemory(instance);
uint32_t default_sbrk_bytes = default_mem ? Runtime::getMemoryNumPages(default_mem) << IR::numBytesPerPageLog2 : 0;
iter->second.instances.emplace_back(std::make_unique<wasm_cache::entry>(jit::entry{instance, module}, default_sbrk_bytes));
pending_result = optional_entry_ref(*iter->second.instances.back().get()); pending_result = optional_entry_ref(*iter->second.instances.back().get());
}); });
} }
...@@ -286,12 +291,12 @@ namespace eosio { namespace chain { ...@@ -286,12 +291,12 @@ namespace eosio { namespace chain {
void return_entry(const digest_type& code_id, wasm_cache::entry& entry) { void return_entry(const digest_type& code_id, wasm_cache::entry& entry) {
// sanitize by reseting the memory that may now be dirty // sanitize by reseting the memory that may now be dirty
auto& info = (*fetch_info(code_id)).get(); auto& info = (*fetch_info(code_id)).get();
if(getDefaultMemory(entry.instance)) { if(getDefaultMemory(entry.jit.instance)) {
char* memstart = &memoryRef<char>( getDefaultMemory(entry.instance), 0 ); char* memstart = &memoryRef<char>( getDefaultMemory(entry.jit.instance), 0 );
memset( memstart + info.mem_end, 0, ((1<<16) - info.mem_end) ); memset( memstart + info.mem_end, 0, ((1<<16) - info.mem_end) );
memcpy( memstart, info.mem_image.data(), info.mem_end); memcpy( memstart, info.mem_image.data(), info.mem_end);
} }
resetGlobalInstances(entry.instance); resetGlobalInstances(entry.jit.instance);
// under a lock, put this entry back in the available instances side of the instances vector // under a lock, put this entry back in the available instances side of the instances vector
with_lock(_cache_lock, [&,this](){ with_lock(_cache_lock, [&,this](){
...@@ -336,7 +341,7 @@ namespace eosio { namespace chain { ...@@ -336,7 +341,7 @@ namespace eosio { namespace chain {
void wasm_cache::checkin(const digest_type& code_id, entry& code ) { void wasm_cache::checkin(const digest_type& code_id, entry& code ) {
MemoryInstance* default_mem = Runtime::getDefaultMemory(code.instance); MemoryInstance* default_mem = Runtime::getDefaultMemory(code.jit.instance);
if(default_mem) if(default_mem)
Runtime::shrinkMemory(default_mem, Runtime::getMemoryNumPages(default_mem) - 1); Runtime::shrinkMemory(default_mem, Runtime::getMemoryNumPages(default_mem) - 1);
_my->return_entry(code_id, code); _my->return_entry(code_id, code);
...@@ -362,15 +367,15 @@ namespace eosio { namespace chain { ...@@ -362,15 +367,15 @@ namespace eosio { namespace chain {
void wasm_interface_impl::call(const string& entry_point, const vector<Value>& args, wasm_cache::entry& code, apply_context& context) void wasm_interface_impl::call(const string& entry_point, const vector<Value>& args, wasm_cache::entry& code, apply_context& context)
try { try {
FunctionInstance* call = asFunctionNullable(getInstanceExport(code.instance,entry_point) ); FunctionInstance* call = asFunctionNullable(getInstanceExport(code.jit.instance,entry_point) );
if( !call ) { if( !call ) {
return; return;
} }
FC_ASSERT( getFunctionType(call)->parameters.size() == args.size() ); FC_ASSERT( getFunctionType(call)->parameters.size() == args.size() );
auto context_guard = scoped_context(current_context, code, context); auto context_guard = scoped_context(current_context, code, context, code.default_sbrk_bytes);
runInstanceStartFunc(code.instance); runInstanceStartFunc(code.jit.instance);
Runtime::invokeFunction(call,args); Runtime::invokeFunction(call,args);
} catch( const Runtime::Exception& e ) { } catch( const Runtime::Exception& e ) {
FC_THROW_EXCEPTION(wasm_execution_error, FC_THROW_EXCEPTION(wasm_execution_error,
...@@ -402,6 +407,15 @@ namespace eosio { namespace chain { ...@@ -402,6 +407,15 @@ namespace eosio { namespace chain {
my->call("error", args, code, context); my->call("error", args, code, context);
} }
wasm_context& common::intrinsics_accessor::get_context(wasm_interface &wasm) {
FC_ASSERT(wasm.my->current_context.valid());
return *wasm.my->current_context;
}
const jit::entry& jit::entry::get(wasm_interface& wasm) {
return common::intrinsics_accessor::get_context(wasm).code.jit;
}
#if defined(assert) #if defined(assert)
#undef assert #undef assert
#endif #endif
...@@ -932,7 +946,7 @@ class memory_api : public context_aware_api { ...@@ -932,7 +946,7 @@ class memory_api : public context_aware_api {
constexpr uint32_t NBPPL2 = IR::numBytesPerPageLog2; constexpr uint32_t NBPPL2 = IR::numBytesPerPageLog2;
constexpr uint32_t MAX_MEM = 1024 * 1024; constexpr uint32_t MAX_MEM = 1024 * 1024;
MemoryInstance* default_mem = Runtime::getDefaultMemory(code.instance); MemoryInstance* default_mem = Runtime::getDefaultMemory(code.jit.instance);
if(!default_mem) if(!default_mem)
throw eosio::chain::page_memory_error(); throw eosio::chain::page_memory_error();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册