Last active
March 5, 2020 12:46
-
-
Save ArnCarveris/fe0743a1023908f97b252949af4dafe8 to your computer and use it in GitHub Desktop.
EnTT VM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "vm.hpp" | |
namespace entt | |
{ | |
template<typename Code> | |
struct vm_encoded | |
{ | |
memory_buffer opcode; | |
memory_buffer bytecode; | |
vm_pc_type pc; | |
}; | |
template<typename Code> | |
struct vm_traits<vm_encoded<Code>> { | |
using code_type = Code; | |
static inline std::size_t size_of(vm_encoded<Code>&) { | |
return sizeof(Code); | |
} | |
template<typename R, typename... Args> | |
static inline std::size_t size_of(vm_encoded<Code>& vm, R(*func)(Args...)) { | |
return size_of(vm) + sizeof(Code); | |
} | |
template<typename Func, typename R, typename... Args> | |
static inline auto make(vm_encoded<Code>& vm, vm_func<R, Args...>& handle, Func&& func) { | |
if (memory_alloc(vm.opcode, handle)) | |
{ | |
memory_ctor(vm.opcode, handle, std::forward<Func>(func)); | |
vm_make<vm_args>(vm, Code(handle.offset / sizeof(std::size_t))); | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
template<typename R, typename... Args> | |
static inline auto func_get(vm_encoded<Code>& vm) { | |
auto func = vm_func<R, Args...>{std::size_t(internal::vm_arg_ref<Code>(vm)) * sizeof(std::size_t)}; | |
return *memory_resolve(vm.opcode, std::move(func)); | |
} | |
template<typename R, typename... Args> | |
static inline auto func_get(vm_encoded<Code>& vm, const vm_func<R, Args...>& func) { | |
return *memory_get(vm.opcode, func); | |
} | |
static inline memory_buffer& code_ref(vm_encoded<Code>& vm) { | |
return vm.bytecode; | |
} | |
static inline memory_buffer& data_ref(vm_encoded<Code>& vm) { | |
return vm.bytecode; | |
} | |
static inline vm_pc_type& pc_ref(vm_encoded<Code>& vm) { | |
return vm.pc; | |
} | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include <cstddef> | |
#include <type_traits> | |
namespace entt | |
{ | |
template<typename Iterator> | |
struct range : private std::pair<Iterator, Iterator> | |
{ | |
range() = default; | |
range(range&&) = default; | |
range(const range&) = default; | |
template<typename First, typename Second> | |
range(First&& first, Second&& second) : | |
std::pair<Iterator, Iterator> { | |
std::forward<First>(first), | |
std::forward<Second>(second) | |
} { } | |
auto begin() const { | |
return first; | |
} | |
auto end() const { | |
return second; | |
} | |
}; | |
namespace internal | |
{ | |
template<std::size_t N> | |
struct choice : choice<N - 1> { }; | |
template<> | |
struct choice<0> {}; | |
template <class F, size_t... Is> | |
constexpr auto index_apply_impl(F&& f, std::index_sequence<Is...>) { | |
return f(std::integral_constant<size_t, Is> {}...); | |
} | |
template <size_t N, class F> | |
constexpr auto index_apply(F&& f) { | |
return index_apply_impl(std::forward<F>(f), std::make_index_sequence<N>{}); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "helpers.hpp" | |
namespace entt | |
{ | |
template<typename Handle> | |
struct memory_handle_traits; | |
using memory_type = std::size_t; | |
struct memory_buffer | |
{ | |
void* data{ nullptr }; | |
memory_type capacity{ 0 }; | |
memory_type used{ 0 }; | |
}; | |
template<typename... Types> | |
struct memory_typelist{ }; | |
template<typename... Types> | |
constexpr const auto& memory_offset(const memory_type idx) { | |
static memory_type cursor{ 0 }; | |
static memory_type table[sizeof...(Types)]{ | |
memory_yield<Types>(cursor)... | |
}; | |
return table[idx]; | |
} | |
template<typename... Types> | |
constexpr const auto& memory_size() { | |
static memory_type ret{ ([]() { | |
return sizeof(std::decay_t<Types>); | |
} () + ... + 0) }; | |
return ret; | |
} | |
template <class F, typename... Types> | |
constexpr auto memory_apply(memory_typelist<Types...>&&, F&& f) { | |
return internal::index_apply<sizeof...(Types)>([&](auto... Idx) { | |
return f(memory_offset<Types...>(Idx)...); | |
}); | |
} | |
template <class F, typename... Types> | |
constexpr auto memory_apply(memory_buffer& buf, const memory_type& offset, memory_typelist<Types...>&&, F&& f) { | |
return internal::index_apply<sizeof...(Types)>([&](auto... Idx) { | |
return f(memory_get<Types>(buf, offset + memory_offset<Types...>(Idx))...); | |
}); | |
} | |
template<typename Handle> | |
inline auto& memory_offset(Handle& handle) { | |
return *internal::memory_handle_offset_ptr_variant(internal::choice<2>{}, handle); | |
} | |
template<typename Handle> | |
inline auto memory_size(const Handle& handle) { | |
return internal::memory_handle_size_variant<Handle>(internal::choice<3>{}, handle); | |
} | |
template<typename Type = void> | |
inline auto memory_get(const memory_buffer& buf, const memory_type& offset) { | |
return reinterpret_cast<std::decay_t<Type>*>(static_cast<std::uint8_t*>(buf.data) + offset); | |
} | |
template<typename Type> | |
constexpr auto memory_yield(memory_type& cursor) { | |
auto offset = cursor; | |
cursor += sizeof(std::decay_t<Type>); | |
return offset; | |
} | |
template<typename Handle> | |
inline auto memory_yield(memory_type& cursor, const Handle& handle) { | |
auto offset = cursor; | |
cursor += memory_size(handle); | |
return offset; | |
} | |
template<typename Type, typename... Args> | |
inline auto memory_ctor(memory_buffer& buf, const memory_type& offset, Args&&... args) { | |
using type = std::decay_t<Type>; | |
return new (memory_get(buf, offset)) type{ std::forward<Args>(args)... }; | |
} | |
template<typename Type, typename... Args> | |
inline auto memory_copy | |
( | |
memory_buffer& src_buf, memory_type& src_offset, | |
memory_buffer& dst_buf, memory_type& dst_offset, | |
Args&&... args | |
) { | |
return memory_ctor<Type>(dst_buf, dst_offset, *memory_get<Type>(src_buf, src_offset), std::forward<Args>(args)...); | |
} | |
template<typename Type> | |
inline auto memory_dtor(const memory_buffer& buf, const memory_type& offset) { | |
using type = std::decay_t<Type>; | |
memory_get<type>(buf, offset)->~type(); | |
return true; | |
} | |
template<typename Handle> | |
inline bool memory_alloc(memory_buffer& buf, Handle& handle) { | |
memory_offset(handle) = buf.used; | |
buf.used += memory_size<Handle>(handle); | |
return buf.used <= buf.capacity; | |
} | |
template<typename Handle> | |
inline bool memory_dealloc(memory_buffer& buf, Handle& handle) { | |
return mmeory_offset(handle) < buf.used; | |
} | |
template<typename Handle, typename... Args> | |
inline auto memory_ctor(memory_buffer& buf, Handle& handle, Args&&... args) { | |
return internal::memory_handle_ctor_variant(internal::choice<2>{}, buf, handle, std::forward<Args>(args)...); | |
} | |
template<typename Handle> | |
inline auto memory_dtor(memory_buffer& buf, Handle& handle) { | |
return internal::memory_handle_dtor_variant(internal::choice<2>{}, buf, handle); | |
} | |
template<typename Handle, typename... Args> | |
inline auto memory_new(memory_buffer& buf, Args&&... args) { | |
auto handle = Handle{}; | |
memory_alloc(buf, handle); | |
memory_ctor(buf, handle, std::forward<Args>(args)...); | |
return handle; | |
} | |
template<typename Handle> | |
inline auto memory_resolve(memory_buffer& buf, Handle* handle) { | |
return internal::memory_handle_resolve_variant(internal::choice<3>{}, buf, handle); | |
} | |
template<typename Handle> | |
inline auto memory_resolve(memory_buffer& buf, const Handle& handle) { | |
return memory_resolve(buf, const_cast<Handle*>(&handle)); | |
} | |
template<typename Handle> | |
inline auto memory_delete(memory_buffer& buf, Handle& handle) { | |
memory_dtor(buf, handle); | |
return memory_dealloc(buf, handle); | |
} | |
namespace internal | |
{ | |
template<typename Handle> | |
using memory_handle_type = typename memory_handle_traits<Handle>::type; | |
template<typename Handle> | |
inline auto memory_handle_offset_ptr_variant(choice<1>, Handle& handle) -> decltype(memory_handle_traits<Handle>::offset_ptr(handle)) { | |
return memory_handle_traits<Handle>::offset_ptr(handle); | |
} | |
template<typename Handle> | |
inline auto memory_handle_offset_ptr_variant(choice<0>, Handle& handle) -> decltype(&handle.offset) { | |
return &handle.offset; | |
} | |
template<typename Handle> | |
inline auto memory_handle_size_variant(choice<2>, const Handle& handle) -> decltype(memory_handle_traits<Handle>::size(handle)) { | |
return memory_handle_traits<Handle>::size(handle); | |
} | |
template<typename Handle> | |
inline auto memory_handle_size_variant(choice<1>, const Handle& handle) -> decltype(sizeof(std::decay_t<memory_handle_type<Handle>>)) { | |
return sizeof(std::decay_t<memory_handle_type<Handle>>); | |
} | |
template<typename Handle> | |
inline auto memory_handle_size_variant(choice<0>, const Handle& handle){ | |
return sizeof(std::decay_t<Handle>); | |
} | |
template<typename Handle, typename... Args> | |
inline auto memory_handle_ctor_variant(choice<1>, memory_buffer& buf, Handle& handle, Args&&... args) -> decltype(memory_handle_traits<Handle>::ctor(buf, handle, std::forward<Args>(args)...)) { | |
return memory_handle_traits<Handle>::ctor(buf, handle, std::forward<Args>(args)...); | |
} | |
template<typename Handle, typename... Args> | |
inline auto memory_handle_ctor_variant(choice<0>, memory_buffer& buf, Handle& handle, Args&&... args) -> decltype(static_cast<memory_handle_type<Handle>*>(nullptr)) { | |
return memory_ctor<memory_handle_type<Handle>>(buf, memory_offset(handle), std::forward<Args>(args)...); | |
} | |
template<typename Handle, typename... Args> | |
inline auto memory_handle_ctor_variant(choice<1>, memory_buffer& buf, Handle& dst, Handle& src, Args&&... args) -> decltype(memory_handle_traits<Handle>::ctor(buf, dst, src, std::forward<Args>(args)...)) { | |
return memory_handle_traits<Handle>::ctor(buf, dst, src, std::forward<Args>(args)...); | |
} | |
template<typename Handle, typename... Args> | |
inline auto memory_handle_ctor_variant(choice<0>, memory_buffer& buf, Handle& dst, Handle& src, Args&&... args) -> decltype(static_cast<memory_handle_type<Handle>*>(nullptr)) { | |
return memory_copy<memory_handle_type<Handle>>(buf, memory_offset(src), buf, memory_offset(dst), std::forward<Args>(args)...); | |
} | |
template<typename Handle> | |
inline auto memory_handle_dtor_variant(choice<1>, memory_buffer& buf, Handle& handle) -> decltype(memory_handle_traits<Handle>::dtor(buf, handle)) { | |
return memory_handle_traits<Handle>::dtor(buf, handle); | |
} | |
template<typename Handle> | |
inline auto memory_handle_dtor_variant(choice<0>, memory_buffer& buf, Handle& handle) -> decltype(static_cast<memory_handle_type<Handle>*>(nullptr), bool{}) { | |
return memory_dtor<memory_handle_type<Handle>>(buf, memory_offset(handle)); | |
} | |
template<typename Handle> | |
inline auto memory_handle_resolve_variant(choice<2>, memory_buffer& buf, Handle* handle) -> decltype(memory_handle_traits<Handle>::resolve(buf, handle)) { | |
return memory_handle_traits<Handle>::resolve(buf, handle); | |
} | |
template<typename Handle> | |
inline auto memory_handle_resolve_variant(choice<1>, memory_buffer& buf, Handle* handle) -> decltype(static_cast<memory_handle_type<Handle>*>(nullptr)) { | |
return memory_get<memory_handle_type<Handle>>(buf, memory_offset(*handle)); | |
} | |
template<typename Handle> | |
inline auto memory_handle_resolve_variant(choice<0>, memory_buffer& buf, Handle* handle) { | |
return handle; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "vm.hpp" | |
namespace entt | |
{ | |
struct vm_simple | |
{ | |
memory_buffer bytecode; | |
vm_pc_type pc; | |
}; | |
template<> | |
struct vm_traits<vm_simple> { | |
static inline std::size_t size_of(vm_simple&) { | |
return sizeof(internal::vm_decoder_type<vm_simple>); | |
} | |
template<typename R, typename... Args> | |
static inline std::size_t size_of(vm_simple& vm, R(*func)(Args...)) { | |
return size_of(vm) + sizeof(func); | |
} | |
static inline memory_buffer& code_ref(vm_simple& vm) { | |
return vm.bytecode; | |
} | |
static inline memory_buffer& data_ref(vm_simple& vm) { | |
return vm.bytecode; | |
} | |
static inline vm_pc_type& pc_ref(vm_simple& vm) { | |
return vm.pc; | |
} | |
}; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "simple.hpp" | |
#include "encoded.hpp" | |
#include <string> | |
#include <memory> | |
namespace entt | |
{ | |
void vm_test_nothing(const std::shared_ptr<int>& ptr) | |
{ | |
assert(true); | |
} | |
int vm_test_variable(int& s, const int& k, const vm_array<float>::range_type& r) | |
{ | |
s *= 2; | |
for (auto& i : r) { | |
i *= 1.3f; | |
i = i; | |
} | |
return s + k; | |
} | |
bool vm_test_abort() | |
{ | |
return false; | |
} | |
void vm_test_unreachable(bool aborted) | |
{ | |
assert(!aborted); | |
} | |
template<typename VM> | |
void vm_test_tmp(VM&& vm) { | |
std::shared_ptr<int> ptr; | |
{ | |
ptr = std::make_shared<int>(1244); | |
auto data = vm_make<vm_label>(vm); | |
auto arr = vm_make<vm_array>(vm, 1.0f, 1.4f, 2.0f); | |
auto variable = vm_make<vm_var>(vm, 123); | |
auto variable_args = vm_make<vm_args>(vm, variable, 345, arr); | |
auto variable_fn = vm_make<vm_var>(vm, 0); | |
auto fn = vm_make<vm_func>(vm, vm_test_abort); | |
auto fn2 = vm_make<vm_func>(vm, fn); | |
auto code = vm_make<vm_label>(vm); | |
vm_compile(vm, vm_test_variable, variable_args, variable_fn); | |
auto nothing_proc = vm_compile(vm, vm_test_nothing, ptr); | |
auto cleanup = vm_make<vm_label>(vm); | |
vm_compile<vm_delete_op>(vm, arr); | |
vm_compile<vm_delete_op>(vm, variable); | |
vm_compile<vm_delete_op>(vm, variable_args); | |
vm_compile<vm_delete_op>(vm, variable_fn); | |
vm_compile<vm_delete_op>(vm, nothing_proc); | |
auto end = vm_make<vm_label>(vm); | |
vm_jump(vm, code); | |
while (vm_until_label(vm, end)) | |
{ | |
vm_step(vm); | |
} | |
auto& s = vm_get(vm, variable); | |
for (auto& f : vm_get(vm, arr)) | |
{ | |
assert(f > 1.0f); | |
} | |
ptr.reset(); | |
} | |
assert(ptr.use_count() == 0); | |
} | |
void vm_test(vm_simple&& vm) { | |
std::string buffer; | |
buffer.resize | |
( | |
vm_size_of<int>() + | |
vm_size_of(vm, vm_test_nothing) + | |
vm_size_of(vm, vm_test_variable) | |
); | |
buffer.resize(buffer.size() * 3); | |
vm.bytecode = memory_buffer{ buffer.data(), buffer.capacity() }; | |
vm_test_tmp(vm); | |
} | |
template<typename Code> | |
void vm_test(vm_encoded<Code>&& vm) { | |
std::string opcodes; | |
std::string bytecodes; | |
opcodes.resize(255); | |
bytecodes.resize( | |
vm_size_of<int>() + | |
vm_size_of(vm, vm_test_nothing) + | |
vm_size_of(vm, vm_test_variable) | |
); | |
bytecodes.resize(bytecodes.size() * 3); | |
vm.opcode = memory_buffer{ opcodes.data(), opcodes.capacity() }; | |
vm.bytecode = memory_buffer{ bytecodes.data(), bytecodes.capacity() }; | |
vm_test_tmp(vm); | |
} | |
void vm_test() { | |
vm_test(vm_simple{}); | |
vm_test(vm_encoded<std::uint8_t>{}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "memory.hpp" | |
namespace entt | |
{ | |
using vm_pc_type = memory_type; | |
template<typename> | |
struct vm_traits; | |
template<template<typename...> class, typename...> | |
struct vm_make_traits; | |
template<typename... Args> | |
struct vm_label | |
{ | |
memory_type offset{ 0 }; | |
}; | |
template<typename Type> | |
struct vm_var | |
{ | |
memory_type offset{ 0 }; | |
}; | |
template<typename Type> | |
struct vm_array | |
{ | |
using range_type = range<Type*>; | |
memory_type offset{ 0 }; | |
memory_type count{ 0 }; | |
}; | |
template<typename... Args> | |
struct vm_args | |
{ | |
memory_type offset{ 0 }; | |
}; | |
template<typename R, typename... Args> | |
struct vm_func | |
{ | |
memory_type offset{ 0 }; | |
}; | |
template<typename VM, typename... Args> | |
struct vm_basic_op | |
{ | |
static constexpr auto compile(VM& vm, Args&&...args) | |
{ | |
return vm_make_traceless<vm_args>(vm, std::forward<Args>(args)...); | |
} | |
static constexpr auto execute(VM& vm, Args&&...args); | |
static constexpr auto input(VM& vm) | |
{ | |
return vm_args<Args...>{vm_pc_ref(vm) }; | |
} | |
static constexpr auto output(VM& vm) | |
{ | |
return vm_args<>{}; | |
} | |
}; | |
template<auto func, typename VM, typename... Args> | |
struct vm_op : vm_basic_op<VM, Args...> | |
{ | |
static constexpr auto execute(VM& vm, Args&&...args) | |
{ | |
return func(vm, std::forward<Args>(args)...); | |
} | |
}; | |
template<typename... Args> | |
struct memory_handle_traits<vm_label<Args...>> { | |
static constexpr auto is_empty = sizeof...(Args) == 0; | |
static constexpr auto is_array = sizeof...(Args) > 1; | |
using internal_type = std::conditional_t<is_array, std::array<memory_type, sizeof...(Args)>, vm_label<Args...>>; | |
static auto size(const vm_label<Args...>& handle) { | |
if constexpr (is_empty) | |
{ | |
return 0; | |
} | |
else | |
{ | |
return sizeof(internal_type); | |
} | |
} | |
static void ctor(memory_buffer& buf, vm_label<Args...>& handle, Args&&... args) { | |
if constexpr (!is_empty) | |
{ | |
constexpr auto conv = [pc = handle.offset + sizeof(internal_type)](auto&& input) | |
{ | |
using input_type = std::decay_t<decltype(input)>; | |
if constexpr (std::is_same_v<input_type, std::nullptr_t>) | |
{ | |
return pc; | |
} | |
else | |
{ | |
return input; | |
} | |
}; | |
memory_ctor<internal_type>(buf, handle.offset, conv(std::forward<Args>(args))...); | |
} | |
} | |
static void dtor(memory_buffer& buf, vm_label<Args...>& handle) { | |
if constexpr (!is_empty) | |
{ | |
memory_dtor<internal_type>(buf, handle.offset); | |
} | |
} | |
}; | |
template<typename Type> | |
struct memory_handle_traits<vm_var<Type>> { | |
using type = Type; | |
}; | |
template<typename Type> | |
struct memory_handle_traits<vm_array<Type>> { | |
static auto size(const vm_array<Type>& handle) { | |
return sizeof(Type) * handle.count; | |
} | |
template<typename... Args> | |
static void ctor(memory_buffer& buf, vm_array<Type>& handle, Args&&... args) { | |
auto offset = handle.offset; | |
handle.count = sizeof...(Args); | |
buf.used = offset + size(handle); | |
(memory_ctor<Type>(buf, memory_yield<Type>(offset), std::forward<Args>(args)), ...); | |
} | |
static auto resolve(memory_buffer& buf, vm_array<Type>* handle) { | |
return typename vm_array<Type>::range_type { | |
memory_get<Type>(buf, handle->offset), | |
memory_get<Type>(buf, handle->offset + sizeof(Type) * handle->count) | |
}; | |
} | |
static void dtor(memory_buffer& buf, vm_array<Type>& handle) { | |
for(auto offset = handle.offset; handle.count > 0; --handle.count) { | |
memory_dtor<Type>(buf, memory_yield<Type>(offset)); | |
} | |
} | |
}; | |
template<typename... Args> | |
struct memory_handle_traits<vm_args<Args...>> { | |
using typelist = memory_typelist<Args...>; | |
static const auto& size(const vm_args<Args...>& handle) { | |
return memory_size<Args...>(); | |
} | |
static void ctor(memory_buffer& buf, vm_args<Args...>& handle, Args&&... args) { | |
memory_apply(typelist{}, [&](auto... o) { | |
(memory_ctor<Args>(buf, handle.offset + o, std::forward<Args>(args)), ...); | |
}); | |
} | |
static void dtor(memory_buffer& buf, vm_args<Args...>& handle) { | |
memory_apply(typelist{}, [&](auto... o) { | |
(memory_dtor<Args>(buf, handle.offset + o), ...); | |
}); | |
} | |
}; | |
template<typename R, typename... Args> | |
struct memory_handle_traits<vm_func<R, Args...>> { | |
using type = R(*)(Args...); | |
}; | |
template<template<typename...> class Handle, typename... Types> | |
struct vm_make_traits | |
{ | |
template<typename... Args> | |
static auto make_handle() { | |
if constexpr (sizeof...(Types) > 0) | |
{ | |
return Handle<Types...>{}; | |
} | |
else | |
{ | |
return Handle<Args...>{}; | |
} | |
} | |
}; | |
template<typename Type, typename... NotAllowed> | |
struct vm_make_traits<vm_var, Type, NotAllowed...> | |
{ | |
static_assert(sizeof...(NotAllowed) == 0, "vm_var: only one `Type` is allowed"); | |
template<typename...> | |
static auto make_handle() { | |
return vm_var<Type>{}; | |
}; | |
}; | |
template<> | |
struct vm_make_traits<vm_var> | |
{ | |
template<typename Type, typename...> | |
static auto make_handle() { | |
return vm_var<Type>{}; | |
}; | |
}; | |
template<> | |
struct vm_make_traits<vm_func> | |
{ | |
template<typename Type, typename... NotAllowed> | |
static auto make_handle() { | |
static_assert(sizeof...(NotAllowed) == 0, "vm_func: only one argument is allowed"); | |
return typename internal::vm_func_traits<std::decay_t<Type>>::handle_type{}; | |
}; | |
}; | |
template<typename Type, typename... NotAllowed> | |
struct vm_make_traits<vm_array, Type, NotAllowed...> | |
{ | |
static_assert(sizeof...(NotAllowed) == 0, "vm_array: only one `Type` is allowed"); | |
template<typename...> | |
static auto make_handle() { | |
return vm_array<Type>{}; | |
}; | |
}; | |
template<> | |
struct vm_make_traits<vm_array> | |
{ | |
template<typename Type, typename...> | |
static auto make_handle() { | |
return vm_array<Type>{}; | |
}; | |
}; | |
namespace internal | |
{ | |
template<typename VM, typename... Args> | |
struct vm_is_self : std::false_type {}; | |
template<typename VM, typename First, typename... Args> | |
struct vm_is_self<VM, First, Args...> : std::bool_constant<std::is_same_v<std::decay_t<VM>, std::decay_t<First>>> {}; | |
template<typename VM, typename... Args> | |
using vm_decoder_type = void(*)(VM&, Args&&...); | |
template<typename...> | |
struct vm_func_traits; | |
template<typename T> | |
struct vm_func_traits<T>: vm_func_traits<decltype(&T::operator())> { | |
}; | |
template<typename R, typename... Args> | |
struct vm_func_traits<R(&)(Args...)> : vm_func_traits<R(Args...)> { | |
}; | |
template<typename R, typename... Args> | |
struct vm_func_traits<R(*)(Args...)> : vm_func_traits<R(Args...)> { | |
}; | |
template<class C, typename R, typename... Args> | |
struct vm_func_traits<R(C::*)(Args...) const> : vm_func_traits<R(Args...)> { | |
}; | |
template<typename R, typename... Args> | |
struct vm_func_traits<vm_func<R, Args...> const> : vm_func_traits<vm_func<R, Args...>> { | |
}; | |
template<typename R, typename... Args> | |
struct vm_func_traits<vm_func<R, Args...>> { | |
using type = vm_func<R, Args...>; | |
using handle_type = type; | |
template<typename VM> | |
static constexpr auto get_variant(choice<1>, VM& vm, const type& func) -> decltype(vm_traits<VM>::func_get(vm, func.offset)) { | |
return vm_traits<VM>::func_get(vm, func.offset); | |
} | |
template<typename VM> | |
static constexpr auto get_variant(choice<0>, VM& vm, const type& func) { | |
return *memory_get(vm_code_ref(vm), func); | |
} | |
template<typename VM> | |
static constexpr auto get(VM& vm) { | |
return get_variant(choice<2>{}, vm, vm_arg_ref<type>(vm)); | |
} | |
}; | |
template<typename R, typename... Args> | |
struct vm_func_traits<R(Args...)> { | |
using type = R(*)(Args...); | |
using handle_type = vm_func<R, Args...>; | |
template<typename VM> | |
static constexpr auto get_variant(choice<1>, VM& vm) -> decltype(vm_traits<VM>::template func_get<R, Args...>(vm)) { | |
return vm_traits<VM>::func_get<R, Args...>(vm); | |
} | |
template<typename VM> | |
static constexpr auto get_variant(choice<0>, VM& vm) { | |
return *memory_get<type>(vm_code_ref(vm), memory_yield<type>(vm_pc_ref(vm))); | |
} | |
template<typename VM> | |
static constexpr auto get(VM& vm) { | |
return get_variant<VM>(choice<2>{}, vm); | |
} | |
}; | |
template<typename... Args> | |
struct vm_args_traits { | |
template<typename VM> | |
static constexpr auto input(VM& vm) { | |
return vm_args<Args...>{ vm_pc_ref(vm) }; | |
} | |
template<typename VM> | |
static constexpr auto output(VM& vm) { | |
return vm_args<>{}; | |
} | |
}; | |
template<typename... InputArgs, typename... OutputArgs> | |
struct vm_args_traits<vm_args<InputArgs...> const, OutputArgs...> : vm_args_traits<vm_args<InputArgs...>, OutputArgs...> { | |
}; | |
template<typename... InputArgs, typename... OutputArgs> | |
struct vm_args_traits<vm_args<InputArgs...>&, OutputArgs...> : vm_args_traits<vm_args<InputArgs...>, OutputArgs...> { | |
}; | |
template<typename... InputArgs, typename... OutputArgs> | |
struct vm_args_traits<vm_args<InputArgs...>, OutputArgs...> { | |
template<typename VM> | |
static constexpr auto input(VM& vm) { | |
return vm_arg_ref<vm_args<InputArgs...>>(vm); | |
} | |
template<typename VM> | |
static constexpr auto output(VM& vm) { | |
return vm_args<OutputArgs...>{ vm_pc_ref(vm) }; | |
} | |
}; | |
template<typename VM, typename Type> | |
constexpr auto vm_trace_make_variant(choice<1>, VM& vm, const Type& instance) -> decltype(vm_traits<VM>::trace_make(vm, instance), void()) { | |
vm_traits<VM>::trace_make(vm, instance); | |
} | |
template<typename VM, typename Type> | |
constexpr void vm_trace_make_variant(choice<0>, VM& vm, const Type& instance) { | |
} | |
template<typename VM, typename Handle, typename... Args> | |
constexpr auto vm_make_variant(choice<1>, VM& vm, Handle& handle, Args&&... args) -> decltype(vm_traits<VM>::make(vm, handle, std::forward<Args>(args)...)) { | |
return vm_traits<VM>::make(vm, handle, std::forward<Args>(args)...); | |
} | |
template<typename VM, typename Handle, typename... Args> | |
constexpr auto vm_make_variant(choice<0>, VM& vm, Handle& handle, Args&&... args) { | |
auto& buf = vm_buf_ref(vm, handle); | |
if (memory_alloc(buf, handle)) | |
{ | |
memory_ctor(buf, handle, std::forward<Args>(args)...); | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
template<typename Type, typename VM> | |
constexpr auto* vm_arg_ptr(VM& vm) { | |
return memory_get<Type>(vm_code_ref(vm), memory_yield<Type>(vm_pc_ref(vm))); | |
} | |
template<typename Type, typename VM> | |
constexpr auto& vm_arg_ref(VM& vm) { | |
return *vm_arg_ptr<Type>(vm); | |
} | |
template<typename VM, typename Func> | |
constexpr auto vm_func_get(VM& vm) { | |
return vm_func_traits<Func>::get(vm); | |
} | |
template<typename VM, typename Type> | |
constexpr auto vm_trace_delete_variant(choice<1>, VM& vm, const Type& instance) -> decltype(vm_traits<VM>::trace_delete(vm, instance), void()) { | |
vm_traits<VM>::trace_delete(vm, instance); | |
} | |
template<typename VM, typename Type> | |
constexpr void vm_trace_delete_variant(choice<0>, VM& vm, const Type& instance) { | |
} | |
} | |
template<typename Type> | |
constexpr std::size_t vm_size_of() { | |
return sizeof(Type); | |
} | |
template<typename VM, typename R, typename... Args> | |
constexpr std::size_t vm_size_of(VM& vm, R(*func)(Args...)) { | |
return vm_traits<VM>::size_of(vm, func) + memory_size<Args...>(); | |
} | |
template<typename VM> | |
constexpr memory_buffer& vm_code_ref(VM& vm) { | |
return vm_traits<VM>::code_ref(vm); | |
} | |
template<typename VM> | |
constexpr memory_buffer& vm_data_ref(VM& vm) { | |
return vm_traits<VM>::data_ref(vm); | |
} | |
template<typename VM> | |
constexpr vm_pc_type& vm_pc_ref(VM& vm) { | |
return vm_traits<VM>::pc_ref(vm); | |
} | |
template<typename VM, typename... Args> | |
constexpr memory_buffer& vm_buf_ref(VM& vm, const vm_label<Args...>&) { | |
return vm_code_ref(vm); | |
} | |
template<typename VM, typename Type> | |
constexpr memory_buffer& vm_buf_ref(VM& vm, const vm_var<Type>&) { | |
return vm_data_ref(vm); | |
} | |
template<typename VM, typename Type> | |
constexpr memory_buffer& vm_buf_ref(VM& vm, const vm_array<Type>&) { | |
return vm_data_ref(vm); | |
} | |
template<typename VM, typename... Args> | |
constexpr memory_buffer& vm_buf_ref(VM& vm, const vm_args<Args...>&) { | |
return vm_code_ref(vm); | |
} | |
template<typename VM, typename R, typename... Args> | |
constexpr memory_buffer& vm_buf_ref(VM& vm, const vm_func<R, Args...>&) { | |
return vm_code_ref(vm); | |
} | |
template<typename VM, typename... Args> | |
constexpr bool vm_until_label(VM& vm, const vm_label<Args...>& label) { | |
return vm_pc_ref(vm) < label.offset; | |
} | |
template<typename VM, typename Type> | |
constexpr auto& vm_get(VM& vm, const vm_var<Type>& var) { | |
return *memory_get<Type>(vm_buf_ref(vm, var), var.offset); | |
} | |
template<typename VM, typename Type> | |
constexpr auto vm_get(VM& vm, const vm_array<Type>& array) { | |
auto& buf = vm_buf_ref(vm, array); | |
return typename vm_array<Type>::range_type{ | |
memory_get<Type>(buf, array.offset), | |
memory_get<Type>(buf, array.offset + sizeof(Type) * array.count) | |
}; | |
} | |
template<typename VM, typename... Args> | |
constexpr auto& vm_get(VM& vm, const vm_label<Args...>& label) { | |
if constexpr (sizeof...(Args) > 1) | |
{ | |
using type = std::array<memory_type, sizeof...(Args)>; | |
return *memory_get<type>(vm_buf_ref(vm, label), label.offset); | |
} | |
else if constexpr (sizeof...(Args) > 0) | |
{ | |
using type = vm_label<Args..>; | |
return *memory_get<type>(vm_buf_ref(vm, label), label.offset); | |
} | |
else | |
{ | |
return label; | |
} | |
} | |
template<template<typename...> class Handle, typename...Types, typename VM, typename... Args> | |
constexpr auto vm_make(VM& vm, Args&&... args) { | |
auto handle = vm_make_traits<Handle, Types...>::make_handle<Args...>(); | |
if (internal::vm_make_variant(internal::choice<2>{}, vm, handle, std::forward<Args>(args)...)) | |
{ | |
vm_trace_make_variant(internal::choice<2>{}, vm, handle); | |
} | |
return handle; | |
} | |
template<template<typename...> class Handle, typename...Types, typename VM, typename... Args> | |
constexpr auto vm_make_traceless(VM& vm, Args&&... args) { | |
auto handle = vm_make_traits<Handle, Types...>::make_handle<Args...>(); | |
internal::vm_make_variant(internal::choice<2>{}, vm, handle, std::forward<Args>(args)...); | |
return handle; | |
} | |
template<typename VM, typename R, typename... FuncArgs, typename... InputArgs, typename... OutputArgs> | |
constexpr void vm_invoke(VM& vm, R(*func)(FuncArgs...), const vm_args<InputArgs...>& input, const vm_args<OutputArgs...>& output) { | |
constexpr auto pull = [](auto&& input) { | |
using input_type = std::decay_t<decltype(input)>; | |
if constexpr (std::is_pointer_v<input_type>) { | |
return std::ref(*input); | |
} | |
else { | |
return input; | |
} | |
}; | |
if constexpr (internal::vm_is_self<VM, FuncArgs...>::value) { | |
vm_pc_ref(vm) = input.offset + memory_size<InputArgs...>(); | |
memory_apply(vm_code_ref(vm), input.offset, memory_typelist<InputArgs...>{}, [&](auto... p) { | |
func(vm, pull(p)...); | |
}); | |
} | |
else if constexpr (std::is_void_v<R> || sizeof...(OutputArgs) == 0) { | |
vm_pc_ref(vm) = input.offset + memory_size<InputArgs...>(); | |
memory_apply(vm_code_ref(vm), input.offset, memory_typelist<InputArgs...>{}, [&](auto... p) { | |
func(pull(memory_resolve(vm_data_ref(vm), p))...); | |
}); | |
} | |
else { | |
vm_pc_ref(vm) = output.offset + memory_size<OutputArgs...>(); | |
auto result = memory_apply(vm_code_ref(vm), input.offset, memory_typelist<InputArgs...>{}, [&](auto... p) { | |
return func(pull(memory_resolve(vm_data_ref(vm), p))...); | |
}); | |
constexpr auto push = [&result](auto& output) { | |
using input_type = std::decay_t<decltype(result)>; | |
using output_type = std::decay_t<decltype(output)>; | |
if constexpr(std::is_same_v<input_type, output_type>) { | |
output = result; | |
} | |
}; | |
memory_apply(memory_typelist<OutputArgs...>{}, [&](auto... o) { | |
(push(*memory_resolve(vm_data_ref(vm), memory_get<OutputArgs>(vm_code_ref(vm), output.offset + o))), ...); | |
}); | |
} | |
} | |
template<typename VM, typename... Args> | |
constexpr void vm_step(VM& vm, Args&&... args) { | |
internal::vm_func_get<VM, internal::vm_decoder_type<VM, Args...>>(vm)(vm, std::forward<Args>(args)...); | |
} | |
template<typename VM, typename Func, typename... Args> | |
constexpr auto vm_compile(VM& vm, Func&& func, Args&&... args) { | |
using func_traits = internal::vm_func_traits<Func>; | |
using args_traits = internal::vm_args_traits<Args...>; | |
vm_make<vm_func>(vm, +[](VM& vm) { | |
auto func = func_traits::get(vm); | |
auto input = args_traits::input(vm); | |
auto output = args_traits::output(vm); | |
vm_invoke(vm, func, input, output); | |
}); | |
vm_make<vm_func>(vm, std::forward<Func>(func)); | |
return vm_make<vm_args>(vm, std::forward<Args>(args)...); | |
} | |
template<template<typename...> class Op, typename VM, typename... Args> | |
constexpr auto vm_compile(VM& vm, Args&&... args) { | |
using op_type = Op<VM, Args...>; | |
vm_make<vm_func>(vm, +[](VM& vm) { | |
auto func = &op_type::execute; | |
auto input = op_type::input(vm); | |
auto output = op_type::output(vm); | |
vm_invoke(vm, func, input, output); | |
}); | |
return op_type::compile(vm, std::forward<Args>(args)...); | |
} | |
template<typename VM, typename Type> | |
constexpr void vm_delete(VM& vm, Type& instance) { | |
internal::vm_trace_delete_variant(internal::choice<2>{}, vm, instance); | |
memory_dtor(vm_buf_ref(vm, instance), instance); | |
} | |
template<typename VM, typename... Args> | |
constexpr void vm_jump(VM& vm, const vm_label<Args...>& label) { | |
vm_pc_ref(vm) = label.offset; | |
} | |
template<typename VM, typename Input, typename... Args> | |
constexpr void vm_jump(VM& vm, const vm_var<Input>& input, const vm_label<Args...>& label) { | |
static_assert(std::is_convertible_v<Input, std::size_t>, "vm_jump: `Input` type must be convertible to `std::size_t`"); | |
static_assert(sizeof...(Args) > 1, "vm_jump: only 2 and more arguments are allowed"); | |
const auto index = std::size_t(vm_get(vm, input)); | |
const auto& table = vm_get(vm, label); | |
if (index < table.size()) | |
{ | |
vm_pc_ref(vm) = table[index]; | |
} | |
} | |
template<typename VM, typename Arg, typename... NotAllowed> | |
struct vm_delete_op : vm_op<&vm_delete<VM, Arg>, VM, Arg> { | |
static_assert(sizeof...(NotAllowed) == 0, "vm_delete_op: only one argument is allowed"); | |
}; | |
template<typename VM, typename... Args> | |
struct vm_jump_op : vm_basic_op<VM, Args...> | |
{ | |
static constexpr auto execute(VM& vm, Args&&...args) | |
{ | |
return vm_jump(vm, std::forward<Args>(args)...); | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment