Skip to content

Instantly share code, notes, and snippets.

@ArnCarveris
Last active March 5, 2020 12:46
Show Gist options
  • Save ArnCarveris/fe0743a1023908f97b252949af4dafe8 to your computer and use it in GitHub Desktop.
Save ArnCarveris/fe0743a1023908f97b252949af4dafe8 to your computer and use it in GitHub Desktop.
EnTT VM
#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;
}
};
}
#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>{});
}
}
}
#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;
}
}
}
#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;
}
};
}
#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>{});
}
}
#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