//// Dis VM// File: runtime.h// Author: arr// #ifndef _DISVM_SRC_INCLUDE_RUNTIME_H_#define _DISVM_SRC_INCLUDE_RUNTIME_H_ #include <cstdint>#include <cassert>#include <atomic>#include <vector>#include <string>#include <memory>#include <deque>#include <unordered_map>#include <mutex>#include <functional>#include "opcodes.h" namespace disvm{ // Forward declaration class vm_t; namespace runtime { // // VM primitive data type definitions // using byte_t = uint8_t; using short_word_t = int16_t; using word_t = int32_t; using short_real_t = float; using real_t = double; using big_t = int64_t; using pointer_t = std::size_t *; using vm_pc_t = word_t; // // VM memory management // // Forward declaration class vm_alloc_t; // Finalizer callback for allocations. using vm_alloc_instance_finalizer_t = void(*)(vm_alloc_t *); // VM type description class type_descriptor_t final { public: // static static vm_alloc_instance_finalizer_t no_finalizer; // Create a type descriptor for type of the supplied size and pointers based on the supplied map. static std::shared_ptr<const type_descriptor_t> create(const word_t size_in_bytes); static std::shared_ptr<const type_descriptor_t> create(const word_t size_in_bytes, const std::vector<byte_t> &pointer_map); static std::shared_ptr<const type_descriptor_t> create( const word_t size_in_bytes, const word_t pointer_map_length, const byte_t *pointer_map, vm_alloc_instance_finalizer_t finalizer = type_descriptor_t::no_finalizer); public: type_descriptor_t(word_t size_in_bytes, word_t map_in_bytes, const byte_t * pointer_map, vm_alloc_instance_finalizer_t finalizer, const char *debug_name); type_descriptor_t(const type_descriptor_t&) = delete; type_descriptor_t& operator=(const type_descriptor_t&) = delete; bool is_equal(const type_descriptor_t *) const; const word_t size_in_bytes; const word_t map_in_bytes; const byte_t * const pointer_map; const vm_alloc_instance_finalizer_t finalizer;#ifndef NDEBUG const char *debug_type_name;#endif }; // Access to type descriptors for VM intrinsic types class intrinsic_type_desc final { public: // Templated type descriptor getter template<typename IntrinsicType> static std::shared_ptr<const type_descriptor_t> type(); }; // VM memory allocation class vm_alloc_t { public: // static static void *operator new(std::size_t sz); static void operator delete(void *ptr); static vm_alloc_t *allocate(std::shared_ptr<const type_descriptor_t> td); static vm_alloc_t *copy(const vm_alloc_t &other); static vm_alloc_t *from_allocation(pointer_t allocation) { if (allocation == nullptr) return nullptr; return reinterpret_cast<vm_alloc_t *>(reinterpret_cast<uint8_t *>(allocation) - sizeof(vm_alloc_t)); } template <typename T> static T *from_allocation(pointer_t allocation) { auto alloc_inst = vm_alloc_t::from_allocation(allocation); assert(alloc_inst == nullptr || alloc_inst->alloc_type->is_equal(intrinsic_type_desc::type<T>().get())); return static_cast<T *>(alloc_inst); } protected: vm_alloc_t(std::shared_ptr<const type_descriptor_t> td); public: virtual ~vm_alloc_t(); std::size_t add_ref(); std::size_t release(); std::size_t get_ref_count() const; std::shared_ptr<const type_descriptor_t> alloc_type; // Reserved for use by the garbage collector. // This should not be accessed by any other component. pointer_t gc_reserved; public: pointer_t get_allocation() const { // Remove const on the this pointer so the offset can be computed. auto non_const_this = const_cast<vm_alloc_t *>(this); return reinterpret_cast<pointer_t>(reinterpret_cast<uint8_t *>(non_const_this) + sizeof(vm_alloc_t)); } template <typename T> T *get_allocation() const { return reinterpret_cast<T *>(get_allocation()); } private: std::atomic<std::size_t> _ref_count; }; // // VM intrinsic type definitions // // Type to contain the maximum UTF-8 character value using rune_t = uint32_t; // VM string type class vm_string_t final : public vm_alloc_t { public: //static static std::shared_ptr<const type_descriptor_t> type_desc(); // Compare two string objects static int compare(const vm_string_t *s1, const vm_string_t *s2); public: vm_string_t(); vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str); vm_string_t(const vm_string_t &s1, const vm_string_t &s2); vm_string_t(const vm_string_t &other, word_t begin_index, word_t end_index); vm_string_t(const vm_string_t &) = delete; ~vm_string_t(); // Returns this string. vm_string_t &append(const vm_string_t &other); // Returns the character length of the string. word_t get_length() const; rune_t get_rune(word_t index) const; void set_rune(word_t index, rune_t value); int compare_to(const vm_string_t *s) const; const char *str() const; private: word_t _length; word_t _length_max; mutable std::mutex _encoded_str_lock; mutable char *_encoded_str; mutable union string_memory_t { uint8_t local[16]; // Optimization for short strings uint8_t *alloc; } _mem; uint8_t _character_size; // 1 byte (ASCII) or 4 bytes (multi-byte) bool _is_alloc; }; // VM array type class vm_array_t final : public vm_alloc_t { public: //static static std::shared_ptr<const type_descriptor_t> type_desc(); public: vm_array_t(std::shared_ptr<const type_descriptor_t> td, word_t length); vm_array_t(vm_array_t &original, word_t begin_index, word_t length); vm_array_t(const vm_string_t *s); ~vm_array_t(); std::shared_ptr<const type_descriptor_t> get_element_type() const; word_t get_length() const; pointer_t at(word_t index) const; template<typename T> T& at(word_t index) const { return *reinterpret_cast<T *>(at(index)); } // Copy the contents of the source array into this array starting at index void copy_from(const vm_array_t &source, word_t this_begin_index); private: std::shared_ptr<const type_descriptor_t> _element_type; vm_array_t *_original; word_t _length; byte_t *_arr; }; // VM list type class vm_list_t final : public vm_alloc_t { public: //static static std::shared_ptr<const type_descriptor_t> type_desc(); public: vm_list_t(std::shared_ptr<const type_descriptor_t> td, vm_list_t *tail = nullptr); ~vm_list_t(); std::shared_ptr<const type_descriptor_t> get_element_type() const; word_t get_length() const; vm_list_t* get_tail() const; void set_tail(vm_list_t *new_tail); // Returns a pointer to the value pointer_t value() const; private: std::shared_ptr<const type_descriptor_t> _element_type; vm_list_t *_tail; // Utilizing a union so that all value types can // be stored without allocating any extra memory. mutable union element_memory_t { pointer_t alloc; uint64_t local; } _mem; const bool _is_alloc; }; // Forward declaration class vm_channel_t; // VM request mutex struct vm_request_mutex_t { vm_request_mutex_t(); std::mutex pending_request_lock; bool pending_request; }; // VM channel request class vm_channel_request_t final { public: // Create a request using the ID of the requesting thread and an associated request mutex. vm_channel_request_t(uint32_t thread_id, vm_request_mutex_t &request_mutex); uint32_t thread_id; // Callback used to indicate the request was handled in the supplied channel. // This is only called in the event the request was not immediately serviced. std::function<void(const vm_channel_t &)> request_handled_callback; // Data of the request pointer_t data; std::reference_wrapper<vm_request_mutex_t> request_mutex; }; // VM channel type class vm_channel_t final : public vm_alloc_t { public: //static static std::shared_ptr<const type_descriptor_t> type_desc(); using data_transfer_func_t = void(*)(pointer_t, pointer_t, const type_descriptor_t *); public: vm_channel_t( std::shared_ptr<const type_descriptor_t> td, data_transfer_func_t transfer, word_t buffer_len); ~vm_channel_t(); std::shared_ptr<const type_descriptor_t> get_data_type() const; // Send data into the channel. // Returns true if the data was sent, otherwise request is queued and false returned. bool send_data(vm_channel_request_t &send_request); // Receive data from the channel. // Returns true if data was received, otherwise request is queued and false returned. bool receive_data(vm_channel_request_t &receive_request); // Cancel an outstanding request from a thread. void cancel_request(uint32_t thread_id); word_t get_buffer_size() const; private: // Try to send data into the channel buffer. // Returns true if data was sent to buffer, otherwise false. bool try_send_to_buffer(vm_channel_request_t &send_request); // Try to receive data from the channel buffer. // Returns true if data was received from buffer, otherwise false. bool try_receive_from_buffer(vm_channel_request_t &receive_request); private: std::mutex _data_lock; std::shared_ptr<const type_descriptor_t> _data_type; data_transfer_func_t _data_transfer; vm_array_t *_data_buffer; uint32_t _data_buffer_head; // Next element to remove from buffer uint32_t _data_buffer_count; // Count of elements in buffer using request_queue_t = std::deque<vm_channel_request_t>; request_queue_t _data_senders; request_queue_t _data_receivers; }; // Alt channel entry. static_assert(sizeof(word_t) == sizeof(pointer_t), "Alt channel stack layout will be invalid."); struct vm_alt_channel_stack_layout_t { word_t alloc; word_t data; }; // Alt channel layout as defined by the specification for the 'alt' instruction. struct vm_alt_stack_layout_t { word_t send_count; word_t receive_count; vm_alt_channel_stack_layout_t channels[1]; }; // Runtime constants namespace runtime_constants { const auto nil = std::size_t{ 0 }; const auto invalid_program_counter = vm_pc_t{ ~0 }; }; // Addressing mode for middle instruction data enum class address_mode_middle_t { none = 0, small_immediate = 1, small_offset_indirect_fp = 2, small_offset_indirect_mp = 3, }; // Middle instruction data struct middle_data_t { address_mode_middle_t mode; word_t register1; }; // Addressing mode for source and destination instruction data enum class address_mode_t { offset_indirect_mp = 0, offset_indirect_fp = 1, immediate = 2, none = 3, offset_double_indirect_mp = 4, offset_double_indirect_fp = 5, reserved_1 = 6, reserved_2 = 7, }; // Source and destination instruction data struct inst_data_generic_t { address_mode_t mode; word_t register1; word_t register2; }; using src_data_t = inst_data_generic_t; using dest_data_t = inst_data_generic_t; // Forward declaration class vm_registers_t; // Instruction execution operation using vm_exec_t = void(*)(vm_registers_t &, vm_t &); // Addressing code - defines addressing mode for all registers // bit 7 6 5 4 3 2 1 0 // m1 m0 s2 s1 s0 d2 d1 d0 using addr_code_t = uint8_t; // Opcode execution operation struct vm_exec_op_t { opcode_t opcode; addr_code_t addr_code; src_data_t source; middle_data_t middle; dest_data_t destination; }; // VM Instruction (code section of module) union vm_instruction_t { vm_exec_op_t op; vm_exec_t native; }; // Flags that dictate the runtime operations of the module // [SPEC] Values past 'shared_module' are not fully documented. enum class runtime_flags_t { must_compile = 1 << 0, dont_compile = 1 << 1, share_module = 1 << 2, // [TODO] Respect this setting reserved_1 = 1 << 3, // DYNMOD has_import_deprecated = 1 << 4, has_handler = 1 << 5, has_import = 1 << 6, // [SPEC] Added to handle built-in modules builtin = 1 << 7, }; // Module header struct header_t { word_t magic_number; struct { word_t length; std::unique_ptr<byte_t[]> signature; } Signature; runtime_flags_t runtime_flag; word_t stack_extent; word_t code_size; word_t data_size; word_t type_size; word_t export_size; vm_pc_t entry_pc; word_t entry_type; // Stack frame type }; // Export function entry struct export_function_t { export_function_t() = default; export_function_t(export_function_t &&); export_function_t(const export_function_t &) = delete; export_function_t &operator=(const export_function_t &) = delete; ~export_function_t(); vm_pc_t pc; word_t frame_type; word_t sig;Lines 1–500 of 965. Load more
// // Dis VM // File: runtime.h // Author: arr // #ifndef _DISVM_SRC_INCLUDE_RUNTIME_H_ #define _DISVM_SRC_INCLUDE_RUNTIME_H_ #include <cstdint> #include <cassert> #include <atomic> #include <vector> #include <string> #include <memory> #include <deque> #include <unordered_map> #include <mutex> #include <functional> #include "opcodes.h" namespace disvm { // Forward declaration class vm_t; namespace runtime { // // VM primitive data type definitions // using byte_t = uint8_t; using short_word_t = int16_t; using word_t = int32_t; using short_real_t = float; using real_t = double; using big_t = int64_t; using pointer_t = std::size_t *; using vm_pc_t = word_t; // // VM memory management // // Forward declaration class vm_alloc_t; // Finalizer callback for allocations. using vm_alloc_instance_finalizer_t = void(*)(vm_alloc_t *); // VM type description class type_descriptor_t final { public: // static static vm_alloc_instance_finalizer_t no_finalizer; // Create a type descriptor for type of the supplied size and pointers based on the supplied map. static std::shared_ptr<const type_descriptor_t> create(const word_t size_in_bytes); static std::shared_ptr<const type_descriptor_t> create(const word_t size_in_bytes, const std::vector<byte_t> &pointer_map); static std::shared_ptr<const type_descriptor_t> create( const word_t size_in_bytes, const word_t pointer_map_length, const byte_t *pointer_map, vm_alloc_instance_finalizer_t finalizer = type_descriptor_t::no_finalizer); public: type_descriptor_t(word_t size_in_bytes, word_t map_in_bytes, const byte_t * pointer_map, vm_alloc_instance_finalizer_t finalizer, const char *debug_name); type_descriptor_t(const type_descriptor_t&) = delete; type_descriptor_t& operator=(const type_descriptor_t&) = delete; bool is_equal(const type_descriptor_t *) const; const word_t size_in_bytes; const word_t map_in_bytes; const byte_t * const pointer_map; const vm_alloc_instance_finalizer_t finalizer; #ifndef NDEBUG const char *debug_type_name; #endif }; // Access to type descriptors for VM intrinsic types class intrinsic_type_desc final { public: // Templated type descriptor getter template<typename IntrinsicType> static std::shared_ptr<const type_descriptor_t> type(); }; // VM memory allocation class vm_alloc_t { public: // static static void *operator new(std::size_t sz); static void operator delete(void *ptr); static vm_alloc_t *allocate(std::shared_ptr<const type_descriptor_t> td); static vm_alloc_t *copy(const vm_alloc_t &other); static vm_alloc_t *from_allocation(pointer_t allocation) { if (allocation == nullptr) return nullptr; return reinterpret_cast<vm_alloc_t *>(reinterpret_cast<uint8_t *>(allocation) - sizeof(vm_alloc_t)); } template <typename T> static T *from_allocation(pointer_t allocation) { auto alloc_inst = vm_alloc_t::from_allocation(allocation); assert(alloc_inst == nullptr || alloc_inst->alloc_type->is_equal(intrinsic_type_desc::type<T>().get())); return static_cast<T *>(alloc_inst); } protected: vm_alloc_t(std::shared_ptr<const type_descriptor_t> td); public: virtual ~vm_alloc_t(); std::size_t add_ref(); std::size_t release(); std::size_t get_ref_count() const; std::shared_ptr<const type_descriptor_t> alloc_type; // Reserved for use by the garbage collector. // This should not be accessed by any other component. pointer_t gc_reserved; public: pointer_t get_allocation() const { // Remove const on the this pointer so the offset can be computed. auto non_const_this = const_cast<vm_alloc_t *>(this); return reinterpret_cast<pointer_t>(reinterpret_cast<uint8_t *>(non_const_this) + sizeof(vm_alloc_t)); } template <typename T> T *get_allocation() const { return reinterpret_cast<T *>(get_allocation()); } private: std::atomic<std::size_t> _ref_count; }; // // VM intrinsic type definitions // // Type to contain the maximum UTF-8 character value using rune_t = uint32_t; // VM string type class vm_string_t final : public vm_alloc_t { public: //static static std::shared_ptr<const type_descriptor_t> type_desc(); // Compare two string objects static int compare(const vm_string_t *s1, const vm_string_t *s2); public: vm_string_t(); vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str); vm_string_t(const vm_string_t &s1, const vm_string_t &s2); vm_string_t(const vm_string_t &other, word_t begin_index, word_t end_index); vm_string_t(const vm_string_t &) = delete; ~vm_string_t(); // Returns this string. vm_string_t &append(const vm_string_t &other); // Returns the character length of the string. word_t get_length() const; rune_t get_rune(word_t index) const; void set_rune(word_t index, rune_t value); int compare_to(const vm_string_t *s) const; const char *str() const; private: word_t _length; word_t _length_max; mutable std::mutex _encoded_str_lock; mutable char *_encoded_str; mutable union string_memory_t { uint8_t local[16]; // Optimization for short strings uint8_t *alloc; } _mem; uint8_t _character_size; // 1 byte (ASCII) or 4 bytes (multi-byte) bool _is_alloc; }; // VM array type class vm_array_t final : public vm_alloc_t { public: //static static std::shared_ptr<const type_descriptor_t> type_desc(); public: vm_array_t(std::shared_ptr<const type_descriptor_t> td, word_t length); vm_array_t(vm_array_t &original, word_t begin_index, word_t length); vm_array_t(const vm_string_t *s); ~vm_array_t(); std::shared_ptr<const type_descriptor_t> get_element_type() const; word_t get_length() const; pointer_t at(word_t index) const; template<typename T> T& at(word_t index) const { return *reinterpret_cast<T *>(at(index)); } // Copy the contents of the source array into this array starting at index void copy_from(const vm_array_t &source, word_t this_begin_index); private: std::shared_ptr<const type_descriptor_t> _element_type; vm_array_t *_original; word_t _length; byte_t *_arr; }; // VM list type class vm_list_t final : public vm_alloc_t { public: //static static std::shared_ptr<const type_descriptor_t> type_desc(); public: vm_list_t(std::shared_ptr<const type_descriptor_t> td, vm_list_t *tail = nullptr); ~vm_list_t(); std::shared_ptr<const type_descriptor_t> get_element_type() const; word_t get_length() const; vm_list_t* get_tail() const; void set_tail(vm_list_t *new_tail); // Returns a pointer to the value pointer_t value() const; private: std::shared_ptr<const type_descriptor_t> _element_type; vm_list_t *_tail; // Utilizing a union so that all value types can // be stored without allocating any extra memory. mutable union element_memory_t { pointer_t alloc; uint64_t local; } _mem; const bool _is_alloc; }; // Forward declaration class vm_channel_t; // VM request mutex struct vm_request_mutex_t { vm_request_mutex_t(); std::mutex pending_request_lock; bool pending_request; }; // VM channel request class vm_channel_request_t final { public: // Create a request using the ID of the requesting thread and an associated request mutex. vm_channel_request_t(uint32_t thread_id, vm_request_mutex_t &request_mutex); uint32_t thread_id; // Callback used to indicate the request was handled in the supplied channel. // This is only called in the event the request was not immediately serviced. std::function<void(const vm_channel_t &)> request_handled_callback; // Data of the request pointer_t data; std::reference_wrapper<vm_request_mutex_t> request_mutex; }; // VM channel type class vm_channel_t final : public vm_alloc_t { public: //static static std::shared_ptr<const type_descriptor_t> type_desc(); using data_transfer_func_t = void(*)(pointer_t, pointer_t, const type_descriptor_t *); public: vm_channel_t( std::shared_ptr<const type_descriptor_t> td, data_transfer_func_t transfer, word_t buffer_len); ~vm_channel_t(); std::shared_ptr<const type_descriptor_t> get_data_type() const; // Send data into the channel. // Returns true if the data was sent, otherwise request is queued and false returned. bool send_data(vm_channel_request_t &send_request); // Receive data from the channel. // Returns true if data was received, otherwise request is queued and false returned. bool receive_data(vm_channel_request_t &receive_request); // Cancel an outstanding request from a thread. void cancel_request(uint32_t thread_id); word_t get_buffer_size() const; private: // Try to send data into the channel buffer. // Returns true if data was sent to buffer, otherwise false. bool try_send_to_buffer(vm_channel_request_t &send_request); // Try to receive data from the channel buffer. // Returns true if data was received from buffer, otherwise false. bool try_receive_from_buffer(vm_channel_request_t &receive_request); private: std::mutex _data_lock; std::shared_ptr<const type_descriptor_t> _data_type; data_transfer_func_t _data_transfer; vm_array_t *_data_buffer; uint32_t _data_buffer_head; // Next element to remove from buffer uint32_t _data_buffer_count; // Count of elements in buffer using request_queue_t = std::deque<vm_channel_request_t>; request_queue_t _data_senders; request_queue_t _data_receivers; }; // Alt channel entry. static_assert(sizeof(word_t) == sizeof(pointer_t), "Alt channel stack layout will be invalid."); struct vm_alt_channel_stack_layout_t { word_t alloc; word_t data; }; // Alt channel layout as defined by the specification for the 'alt' instruction. struct vm_alt_stack_layout_t { word_t send_count; word_t receive_count; vm_alt_channel_stack_layout_t channels[1]; }; // Runtime constants namespace runtime_constants { const auto nil = std::size_t{ 0 }; const auto invalid_program_counter = vm_pc_t{ ~0 }; }; // Addressing mode for middle instruction data enum class address_mode_middle_t { none = 0, small_immediate = 1, small_offset_indirect_fp = 2, small_offset_indirect_mp = 3, }; // Middle instruction data struct middle_data_t { address_mode_middle_t mode; word_t register1; }; // Addressing mode for source and destination instruction data enum class address_mode_t { offset_indirect_mp = 0, offset_indirect_fp = 1, immediate = 2, none = 3, offset_double_indirect_mp = 4, offset_double_indirect_fp = 5, reserved_1 = 6, reserved_2 = 7, }; // Source and destination instruction data struct inst_data_generic_t { address_mode_t mode; word_t register1; word_t register2; }; using src_data_t = inst_data_generic_t; using dest_data_t = inst_data_generic_t; // Forward declaration class vm_registers_t; // Instruction execution operation using vm_exec_t = void(*)(vm_registers_t &, vm_t &); // Addressing code - defines addressing mode for all registers // bit 7 6 5 4 3 2 1 0 // m1 m0 s2 s1 s0 d2 d1 d0 using addr_code_t = uint8_t; // Opcode execution operation struct vm_exec_op_t { opcode_t opcode; addr_code_t addr_code; src_data_t source; middle_data_t middle; dest_data_t destination; }; // VM Instruction (code section of module) union vm_instruction_t { vm_exec_op_t op; vm_exec_t native; }; // Flags that dictate the runtime operations of the module // [SPEC] Values past 'shared_module' are not fully documented. enum class runtime_flags_t { must_compile = 1 << 0, dont_compile = 1 << 1, share_module = 1 << 2, // [TODO] Respect this setting reserved_1 = 1 << 3, // DYNMOD has_import_deprecated = 1 << 4, has_handler = 1 << 5, has_import = 1 << 6, // [SPEC] Added to handle built-in modules builtin = 1 << 7, }; // Module header struct header_t { word_t magic_number; struct { word_t length; std::unique_ptr<byte_t[]> signature; } Signature; runtime_flags_t runtime_flag; word_t stack_extent; word_t code_size; word_t data_size; word_t type_size; word_t export_size; vm_pc_t entry_pc; word_t entry_type; // Stack frame type }; // Export function entry struct export_function_t { export_function_t() = default; export_function_t(export_function_t &&); export_function_t(const export_function_t &) = delete; export_function_t &operator=(const export_function_t &) = delete; ~export_function_t(); vm_pc_t pc; word_t frame_type; word_t sig;