History

Initial public commit for Dis VM

arr committed 10 years ago

Commit f95902eNo parent (first commit)40 files · +9358 −0Browse files

Showing the changes to src/include (10 of 40 files). Show all

Unified · Split

10 files
  1. src/include/builtin_module.h +66 −0
  2. src/include/debug.h +99 −0
  3. src/include/disvm.h +98 −0
  4. src/include/exceptions.h +137 −0
  5. src/include/module_reader.h +29 −0
  6. src/include/opcodes.h +199 −0
  7. src/include/runtime.h +846 −0
  8. src/include/utf8.h +57 −0
  9. src/include/utils.h +47 −0
  10. src/include/vm_memory.h +109 −0
src/include/builtin_module.h+66−0
@@ -0,0 +1,66 @@
1+//
2+// Dis VM
3+// File: builtin_module.h
4+// Author: arr
5+//
6+
7+#ifndef _DISVM_SRC_INCLUDE_BUILTIN_MODULE_H_
8+#define _DISVM_SRC_INCLUDE_BUILTIN_MODULE_H_
9+
10+#include <cstdint>
11+#include <memory>
12+#include "runtime.h"
13+
14+#define BUILTIN_MODULE_PREFIX_STR "$"
15+#define BUILTIN_MODULE_PREFIX_CHAR '$'
16+
17+namespace disvm
18+{
19+ namespace runtime
20+ {
21+ namespace builtin
22+ {
23+ // Called to initialize built-in modules.
24+ // This will be called once per process hosting a VM.
25+ void initialize_builtin_modules();
26+
27+ // Defined as 'Runtab' in Inferno (limbo/stubs.c)
28+ struct vm_runtab_t
29+ {
30+ // Function identification
31+ char *name;
32+ uint32_t sig;
33+
34+ // Function pointer
35+ vm_exec_t fn;
36+
37+ // Function frame type
38+ word_t frame_size;
39+ word_t frame_pointer_count;
40+ byte_t frame_pointer_map[16];
41+ };
42+
43+ // Called by the built-in module during initialization
44+ void register_module_exports(const char *name, word_t table_length, const vm_runtab_t *module_runtime_table);
45+
46+ // Get the supplied built-in module.
47+ std::shared_ptr<vm_module_t> get_builtin_module(const char *name);
48+ }
49+
50+ // [SPEC] All allocated frames have the following base layout.
51+ // See 'struct Frame' in Inferno (include/interp.h)
52+ struct vm_frame_base_alloc_t
53+ {
54+ vm_pc_t p_pc; // Previous PC
55+ vm_frame_t *p_fp; // Previous frame pointer
56+ vm_module_ref_t *p_mr; // Previous module ref
57+ pointer_t reserved; // Reserved for VM
58+ };
59+
60+ static_assert(
61+ sizeof(vm_frame_base_alloc_t) == runtime_constants::default_register_count * runtime_constants::default_register_size_bytes,
62+ "Default register allocation should match base allocation type");
63+ }
64+}
65+
66+#endif // _DISVM_SRC_INCLUDE_BUILTIN_MODULE_H_
src/include/debug.h+99−0
@@ -0,0 +1,99 @@
1+//
2+// Dis VM
3+// File: debug.h
4+// Author: arr
5+//
6+
7+#ifndef _DISVM_SRC_INCLUDE_DEBUG_H_
8+#define _DISVM_SRC_INCLUDE_DEBUG_H_
9+
10+#include <array>
11+#include <cstdarg>
12+#include <cassert>
13+
14+namespace disvm
15+{
16+ namespace debug
17+ {
18+ // Components that have tracing
19+ enum class component_trace_t
20+ {
21+ builtin,
22+ module,
23+ thread,
24+ stack,
25+ channel,
26+ exception,
27+ memory, // Noisy component
28+ addressing, // Noisy component [DEBUG build only]
29+
30+ // Potentially logged from a VM extension.
31+ scheduler,
32+ garbage_collector,
33+
34+ // Tracking purposes
35+ last = garbage_collector
36+ };
37+
38+ // Trace component enable state.
39+ extern std::array<bool, static_cast<std::size_t>(disvm::debug::component_trace_t::last) + 1> trace_enabled;
40+
41+ // Set tracing for a component
42+ inline void set_component_tracing(const component_trace_t c, bool is_enabled)
43+ {
44+ trace_enabled[static_cast<const std::size_t>(c)] = is_enabled;
45+ }
46+
47+ // Check if tracing is enabled on a component (compile time optimization)
48+ template<component_trace_t C>
49+ bool is_component_tracing_enabled()
50+ {
51+ return trace_enabled[static_cast<const std::size_t>(C)];
52+ }
53+
54+ // Check if tracing is enabled on a component
55+ inline bool is_component_tracing_enabled(const component_trace_t c)
56+ {
57+ return trace_enabled[static_cast<const std::size_t>(c)];
58+ }
59+
60+ // Message log level
61+ enum class log_level_t
62+ {
63+ warning,
64+ debug,
65+ };
66+
67+ using log_callback_t = void(*)(const component_trace_t origin, const log_level_t level, const char *msg_fmt, std::va_list args);
68+
69+ // Set the logging callback
70+ // Returns the previous callback.
71+ log_callback_t set_logging_callback(log_callback_t cb);
72+
73+ // Log a message
74+ void log_msg(const component_trace_t origin, const log_level_t level, const char *msg_fmt, ...);
75+
76+ //
77+ // Memory debugging
78+ //
79+
80+ inline void assign_debug_memory(void *dest, std::size_t size_in_bytes)
81+ {
82+#ifndef NDEBUG
83+ const auto debug_memory = int{ 0xda }; // Dante Alighieri's initials
84+ std::memset(dest, debug_memory, size_in_bytes);
85+#endif
86+ }
87+
88+ template<typename T>
89+ void assign_debug_pointer(T **t)
90+ {
91+#ifndef NDEBUG
92+ assert(t != nullptr);
93+ *t = reinterpret_cast<T *>(0xbada110c);
94+#endif
95+ }
96+ }
97+}
98+
99+#endif // _DISVM_SRC_INCLUDE_DEBUG_H_
src/include/disvm.h+98−0
@@ -0,0 +1,98 @@
1+//
2+// Dis VM
3+// File: disvm.h
4+// Author: arr
5+//
6+
7+#ifndef _DISVM_SRC_INCLUDE_DISVM_H_
8+#define _DISVM_SRC_INCLUDE_DISVM_H_
9+
10+#include "runtime.h"
11+#include <iosfwd>
12+#include <memory>
13+#include <functional>
14+#include <mutex>
15+#include <forward_list>
16+
17+namespace disvm
18+{
19+ // Read in the module at the supplied path
20+ std::unique_ptr<runtime::vm_module_t> read_module(const char *path);
21+
22+ // Read in a module from the supplied stream
23+ std::unique_ptr<runtime::vm_module_t> read_module(std::istream &data);
24+
25+ template <typename C>
26+ using create_vm_interface_callback_t = std::function<std::unique_ptr<C>(vm_t &)>;
27+
28+ // Virtual machine
29+ class vm_t final
30+ {
31+ public: // static
32+ static const auto root_vm_thread_id = uint32_t{ 0 };
33+
34+ public:
35+ explicit vm_t(
36+ create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler = nullptr,
37+ create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc = nullptr);
38+
39+ explicit vm_t(uint32_t thread_pool_size, uint32_t thread_quanta);
40+
41+ ~vm_t();
42+
43+ public:
44+ // Begin execution of the module at the supplied path
45+ const runtime::vm_thread_t& exec(const char *path);
46+
47+ // Begin execution of the supplied module
48+ const runtime::vm_thread_t& exec(std::unique_ptr<runtime::vm_module_t> entry_module);
49+
50+ // Begin execution of a child thread in the supplied module at the indicated program counter
51+ const runtime::vm_thread_t& fork(
52+ const runtime::vm_thread_t &parent,
53+ runtime::vm_module_ref_t &module_ref,
54+ const runtime::vm_frame_t &initial_frame,
55+ runtime::vm_pc_t initial_pc);
56+
57+ // Load the module at the supplied path into the virtual machine
58+ std::shared_ptr<const runtime::vm_module_t> load_module(const char *path);
59+
60+ // Access the scheduler control for this VM.
61+ runtime::vm_scheduler_control_t &get_scheduler_control() const;
62+
63+ // Access the garbage collector for this VM.
64+ runtime::vm_garbage_collector_t &get_garbage_collector() const;
65+
66+ // Spin and sleep until the VM is idle, then return.
67+ // Idle is defined as no vm threads executing, scheduled to be executed, or blocked.
68+ void spin_sleep_till_idle(std::chrono::milliseconds sleep_interval) const;
69+
70+ private:
71+ // Loaded module reference
72+ class loaded_module_t final
73+ {
74+ public:
75+ loaded_module_t(std::unique_ptr<runtime::vm_string_t> origin, std::shared_ptr<const runtime::vm_module_t> module);
76+ loaded_module_t(const loaded_module_t &) = delete;
77+ loaded_module_t &operator=(const loaded_module_t &) = delete;
78+
79+ loaded_module_t(loaded_module_t &&);
80+ ~loaded_module_t();
81+
82+ // Origin of module
83+ std::unique_ptr<runtime::vm_string_t> origin;
84+
85+ // Weak reference to a loaded module
86+ std::weak_ptr<const runtime::vm_module_t> module;
87+ };
88+
89+ using loaded_modules_t = std::forward_list<loaded_module_t>;
90+ std::mutex _modules_lock;
91+ loaded_modules_t _modules;
92+
93+ std::unique_ptr<runtime::vm_scheduler_t> _scheduler;
94+ std::unique_ptr<runtime::vm_garbage_collector_t> _gc;
95+ };
96+}
97+
98+#endif // _DISVM_SRC_INCLUDE_DISVM_H_
src/include/exceptions.h+137−0
@@ -0,0 +1,137 @@
1+//
2+// Dis VM
3+// File: exceptions.h
4+// Author: arr
5+//
6+
7+#ifndef _DISVM_SRC_INCLUDE_EXCEPTIONS_H_
8+#define _DISVM_SRC_INCLUDE_EXCEPTIONS_H_
9+
10+#include <cassert>
11+#include <string>
12+#include <stdexcept>
13+#include "runtime.h"
14+
15+namespace disvm
16+{
17+ namespace runtime
18+ {
19+ class vm_user_exception : public std::runtime_error
20+ {
21+ public:
22+ explicit vm_user_exception(const char *message)
23+ : std::runtime_error(message)
24+ { }
25+ };
26+
27+ class vm_system_exception : public std::runtime_error
28+ {
29+ public:
30+ explicit vm_system_exception(const char *message)
31+ : std::runtime_error(message)
32+ { }
33+ };
34+
35+ class vm_module_exception : public vm_system_exception
36+ {
37+ public:
38+ explicit vm_module_exception(const char *message)
39+ : vm_system_exception(message)
40+ { }
41+ };
42+
43+ class module_reader_exception final : public vm_module_exception
44+ {
45+ public:
46+ explicit module_reader_exception()
47+ : vm_module_exception("Invalid module")
48+ { }
49+
50+ explicit module_reader_exception(const char *message)
51+ : vm_module_exception(message)
52+ { }
53+ };
54+
55+ class divide_by_zero final : public vm_user_exception
56+ {
57+ public:
58+ explicit divide_by_zero()
59+ : vm_user_exception("Divide by 0")
60+ { }
61+ };
62+
63+ class invalid_utf8 : public vm_user_exception
64+ {
65+ public:
66+ explicit invalid_utf8()
67+ : vm_user_exception("Invalid UTF-8 string")
68+ { }
69+ };
70+
71+ class dereference_nil final : public vm_user_exception
72+ {
73+ public:
74+ explicit dereference_nil()
75+ : vm_user_exception("Dereference of nil value")
76+ , operation{ nullptr }
77+ { }
78+
79+ explicit dereference_nil(const char *operation)
80+ : vm_user_exception("Dereference of nil value")
81+ , operation{ operation }
82+ { }
83+
84+ const char *operation;
85+ };
86+
87+ class type_violation : public vm_user_exception
88+ {
89+ public:
90+ explicit type_violation()
91+ : vm_user_exception("Inconsistent types in operation")
92+ { }
93+ };
94+
95+ class out_of_range_memory final : public vm_user_exception
96+ {
97+ public:
98+ explicit out_of_range_memory()
99+ : vm_user_exception("Out of range access")
100+ , invalid_value{ 0 }
101+ , range_defined{ false }
102+ , valid_max{ 0 }
103+ , valid_min{ 0 }
104+ { }
105+
106+ explicit out_of_range_memory(word_t valid_min, word_t valid_max, word_t invalid_valid)
107+ : vm_user_exception("Out of range access")
108+ , invalid_value{ invalid_valid }
109+ , range_defined{ true }
110+ , valid_max{ valid_max }
111+ , valid_min{ valid_min }
112+ { }
113+
114+ const bool range_defined;
115+ const word_t valid_min;
116+ const word_t valid_max;
117+ const word_t invalid_value;
118+ };
119+
120+ class unhandled_user_exception final : public vm_user_exception
121+ {
122+ public:
123+ explicit unhandled_user_exception(const char *exception_id, const char *module_name, vm_pc_t current_pc)
124+ : vm_user_exception("Unhandled user exception")
125+ , exception_id{ exception_id }
126+ , module_name{ module_name }
127+ , program_counter{ current_pc }
128+ { }
129+
130+ const char *exception_id;
131+ const char *module_name;
132+ const vm_pc_t program_counter;
133+ };
134+ }
135+}
136+
137+#endif // _DISVM_SRC_INCLUDE_EXCEPTIONS_H_
src/include/module_reader.h+29−0
@@ -0,0 +1,29 @@
1+//
2+// Dis VM
3+// File: module_reader.h
4+// Author: arr
5+//
6+
7+#ifndef _DISVM_SRC_INCLUDE_MODULE_READER_H_
8+#define _DISVM_SRC_INCLUDE_MODULE_READER_H_
9+
10+#include <cstdint>
11+#include "runtime.h"
12+
13+namespace disvm
14+{
15+ namespace format
16+ {
17+ using operand_t = int32_t;
18+ static_assert(sizeof(operand_t) == sizeof(runtime::word_t), "Operand and word should be the same size");
19+ static_assert(sizeof(operand_t) == sizeof(runtime::vm_pc_t), "Operand and VM program counter type should be the same size");
20+
21+ struct magic_number_constants final
22+ {
23+ static const auto xmagic = operand_t{ 819248 }; // Normal module (0xC8030)
24+ static const auto smagic = operand_t{ 923426 }; // Signed module (0xE1722)
25+ };
26+ }
27+}
28+
29+#endif // _DISVM_SRC_INCLUDE_MODULE_READER_H_
src/include/opcodes.h+199−0
@@ -0,0 +1,199 @@
1+//
2+// Dis VM
3+// File: opcodes.h
4+// Author: arr
5+//
6+
7+#ifndef _DISVM_SRC_INCLUDE_OPCODES_H_
8+#define _DISVM_SRC_INCLUDE_OPCODES_H_
9+
10+#include <cstdint>
11+
12+namespace disvm
13+{
14+ // Instruction codes for the Dis VM
15+ enum class opcode_t
16+ {
17+ invalid = 0,
18+ alt,
19+ nbalt,
20+ goto_,
21+ call,
22+ frame,
23+ spawn,
24+ runt,
25+ load,
26+ mcall,
27+ mspawn,
28+ mframe,
29+ ret,
30+ jmp,
31+ casew,
32+ exit,
33+ new_,
34+ newa,
35+ newcb,
36+ newcw,
37+ newcf,
38+ newcp,
39+ newcm,
40+ newcmp,
41+ send,
42+ recv,
43+ consb,
44+ consw,
45+ consp,
46+ consf,
47+ consm,
48+ consmp = 0x1f,
49+ headb,
50+ headw,
51+ headp,
52+ headf,
53+ headm,
54+ headmp,
55+ tail,
56+ lea,
57+ indx,
58+ movp,
59+ movm,
60+ movmp,
61+ movb,
62+ movw,
63+ movf,
64+ cvtbw,
65+ cvtwb,
66+ cvtfw,
67+ cvtwf,
68+ cvtca,
69+ cvtac,
70+ cvtwc,
71+ cvtcw,
72+ cvtfc,
73+ cvtcf,
74+ addb,
75+ addw,
76+ addf,
77+ subb,
78+ subw,
79+ subf,
80+ mulb = 0x3f,
81+ mulw,
82+ mulf,
83+ divb,
84+ divw,
85+ divf,
86+ modw,
87+ modb,
88+ andb,
89+ andw,
90+ orb,
91+ orw,
92+ xorb,
93+ xorw,
94+ shlb,
95+ shlw,
96+ shrb,
97+ shrw,
98+ insc,
99+ indc,
100+ addc,
101+ lenc,
102+ lena,
103+ lenl,
104+ beqb,
105+ bneb,
106+ bltb,
107+ bleb,
108+ bgtb,
109+ bgeb,
110+ beqw,
111+ bnew,
112+ bltw = 0x5f,
113+ blew,
114+ bgtw,
115+ bgew,
116+ beqf,
117+ bnef,
118+ bltf,
119+ blef,
120+ bgtf,
121+ bgef,
122+ beqc,
123+ bnec,
124+ bltc,
125+ blec,
126+ bgtc,
127+ bgec,
128+ slicea,
129+ slicela,
130+ slicec,
131+ indw,
132+ indf,
133+ indb,
134+ negf,
135+ movl,
136+ addl,
137+ subl,
138+ divl,
139+ modl,
140+ mull,
141+ andl,
142+ orl,
143+ xorl,
144+ shll = 0x7f,
145+ shrl,
146+ bnel,
147+ bltl,
148+ blel,
149+ bgtl,
150+ bgel,
151+ beql,
152+ cvtlf,
153+ cvtfl,
154+ cvtlw,
155+ cvtwl,
156+ cvtlc,
157+ cvtcl,
158+ headl,
159+ consl,
160+ newcl,
161+ casec,
162+ indl,
163+ movpc,
164+ tcmp,
165+ mnewz,
166+ cvtrf,
167+ cvtfr,
168+ cvtws,
169+ cvtsw,
170+ lsrw,
171+ lsrl,
172+ eclr, // not used
173+ newz,
174+ newaz,
175+ raise,
176+ casel,
177+ mulx,
178+ divx,
179+ cvtxx,
180+ mulx0,
181+ divx0,
182+ cvtxx0,
183+ mulx1,
184+ divx1,
185+ cvtxx1,
186+ cvtfx,
187+ cvtxf,
188+ expw,
189+ expl,
190+ expf,
191+ self,
192+
193+ // markers
194+ first_opcode = invalid,
195+ last_opcode = self
196+ };
197+}
198+
199+#endif // _DISVM_SRC_INCLUDE_OPCODES_H_
src/include/runtime.h+846−0
@@ -0,0 +1,846 @@
1+//
2+// Dis VM
3+// File: runtime.h
4+// Author: arr
5+//
6+
7+#ifndef _DISVM_SRC_INCLUDE_RUNTIME_H_
8+#define _DISVM_SRC_INCLUDE_RUNTIME_H_
9+
10+#include <cstdint>
11+#include <cassert>
12+#include <atomic>
13+#include <vector>
14+#include <string>
15+#include <memory>
16+#include <deque>
17+#include <unordered_map>
18+#include <mutex>
19+#include <functional>
20+#include "opcodes.h"
21+
22+namespace disvm
23+{
24+ // VM forward declation
25+ class vm_t;
26+
27+ namespace runtime
28+ {
29+ //
30+ // VM primitive data type definitions
31+ //
32+
33+ using byte_t = uint8_t;
34+ using short_word_t = int16_t;
35+ using word_t = int32_t;
36+ using short_real_t = float;
37+ using real_t = double;
38+ using big_t = int64_t;
39+ using pointer_t = std::size_t *;
40+ using vm_pc_t = word_t;
41+
42+ //
43+ // VM memory management
44+ //
45+
46+ // VM forward declation
47+ class vm_alloc_t;
48+
49+ // Finalizer callback for allocations.
50+ using vm_alloc_instance_finalizer_t = void(*)(vm_alloc_t *);
51+
52+ // VM type description
53+ class type_descriptor_t final
54+ {
55+ public: // static
56+ static vm_alloc_instance_finalizer_t no_finalizer;
57+
58+ // Create a type descriptor for type of the supplied size and pointers based on the supplied map.
59+ static std::shared_ptr<const type_descriptor_t> create(const word_t size);
60+ static std::shared_ptr<const type_descriptor_t> create(const word_t size, const std::vector<byte_t> &pointer_map);
61+ static std::shared_ptr<const type_descriptor_t> create(
62+ const word_t size,
63+ const word_t pointer_map_length,
64+ const byte_t *pointer_map,
65+ vm_alloc_instance_finalizer_t finalizer = type_descriptor_t::no_finalizer);
66+
67+ private:
68+ type_descriptor_t(word_t size_in_bytes, word_t map_in_bytes, const byte_t * pointer_map, vm_alloc_instance_finalizer_t finalizer);
69+
70+ public:
71+ type_descriptor_t(const type_descriptor_t&) = delete;
72+ type_descriptor_t& operator=(const type_descriptor_t&) = delete;
73+
74+ const word_t size_in_bytes;
75+ const word_t map_in_bytes;
76+ const byte_t * const pointer_map;
77+ const vm_alloc_instance_finalizer_t finalizer;
78+ };
79+
80+ // Access to type descriptors for VM intrinsic types.
81+ class intrinsic_type_desc final
82+ {
83+ private:
84+ friend vm_t;
85+ static std::atomic_bool is_initialized;
86+ static void initialize();
87+
88+ public:
89+ // Templated type descriptor getter
90+ template<typename IntrinsicType>
91+ static std::shared_ptr<const type_descriptor_t> type();
92+ };
93+
94+ // Type used to indicate allocated memory should be zeroed out.
95+ struct zero_memory_t
96+ { };
97+
98+ // VM memory allocation
99+ class vm_alloc_t
100+ {
101+ public: // static
102+ static const zero_memory_t zero_memory;
103+
104+ static void *operator new(std::size_t sz);
105+ static void operator delete(void *ptr);
106+
107+ static vm_alloc_t *allocate(std::shared_ptr<const type_descriptor_t> td);
108+ static vm_alloc_t *allocate(std::shared_ptr<const type_descriptor_t> td, const zero_memory_t &);
109+ static vm_alloc_t *copy(const vm_alloc_t &other);
110+ static vm_alloc_t *from_allocation(pointer_t allocation);
111+
112+ template <typename T>
113+ static T *from_allocation(pointer_t allocation)
114+ {
115+ auto alloc_inst = vm_alloc_t::from_allocation(allocation);
116+ assert(alloc_inst == nullptr || alloc_inst->alloc_type == intrinsic_type_desc::type<T>());
117+ return dynamic_cast<T *>(alloc_inst);
118+ }
119+
120+ protected:
121+ vm_alloc_t(std::shared_ptr<const type_descriptor_t> td);
122+
123+ public:
124+ virtual ~vm_alloc_t();
125+
126+ std::size_t add_ref();
127+ std::size_t release();
128+ std::size_t get_ref_count() const;
129+
130+ std::shared_ptr<const type_descriptor_t> alloc_type;
131+
132+ // Reserved for use by the garbage collector.
133+ // This should not be accessed by any other component.
134+ pointer_t gc_reserved;
135+
136+ public:
137+ pointer_t allocation() const;
138+
139+ private:
140+ std::atomic<std::size_t> _ref_count;
141+ };
142+
143+ //
144+ // VM intrinsic type definitions
145+ //
146+
147+ // Type to contain the maximum UTF-8 character value
148+ using rune_t = uint32_t;
149+
150+ // VM string type
151+ class vm_string_t final : public vm_alloc_t
152+ {
153+ public: //static
154+ static std::shared_ptr<const type_descriptor_t> type_desc();
155+
156+ // Compare two string objects
157+ static int compare(const vm_string_t *s1, const vm_string_t *s2);
158+
159+ public:
160+ vm_string_t();
161+ vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str);
162+ vm_string_t(const vm_string_t &s1, const vm_string_t &s2);
163+ vm_string_t(const vm_string_t &other, word_t begin_index, word_t end_index);
164+ vm_string_t(const vm_string_t &) = delete;
165+ ~vm_string_t();
166+
167+ // Returns this string.
168+ vm_string_t &append(const vm_string_t &other);
169+
170+ word_t get_length() const;
171+
172+ rune_t get_rune(word_t index) const;
173+
174+ void set_rune(word_t index, rune_t value);
175+
176+ int compare_to(const vm_string_t *s) const;
177+
178+ const char *str() const;
179+
180+ private:
181+ word_t _length;
182+ word_t _length_max;
183+ mutable std::mutex _encoded_str_lock;
184+ mutable char *_encoded_str;
185+ mutable union string_memory_t
186+ {
187+ uint8_t local[16]; // Optimization for short strings
188+ uint8_t *alloc;
189+ } _mem;
190+
191+ uint8_t _character_size; // 1 byte (ASCII) or 4 bytes (multi-byte)
192+ bool _is_alloc;
193+ };
194+
195+ // VM array type
196+ class vm_array_t final : public vm_alloc_t
197+ {
198+ public: //static
199+ static std::shared_ptr<const type_descriptor_t> type_desc();
200+
201+ public:
202+ vm_array_t(std::shared_ptr<const type_descriptor_t> td, word_t count);
203+ vm_array_t(std::shared_ptr<const type_descriptor_t> td, word_t count, const zero_memory_t&);
204+ vm_array_t(vm_array_t *original, word_t begin_index, word_t count);
205+ vm_array_t(const vm_string_t *s);
206+ ~vm_array_t();
207+
208+ std::shared_ptr<const type_descriptor_t> get_element_type() const;
209+
210+ word_t get_length() const;
211+
212+ pointer_t at(word_t index) const;
213+
214+ template<typename T>
215+ T& at(word_t index) const
216+ {
217+ return *reinterpret_cast<T *>(at(index));
218+ }
219+
220+ // Copy the contents of the source array into this array starting at index
221+ void copy_from(const vm_array_t &source, word_t this_begin_index);
222+
223+ private:
224+ std::shared_ptr<const type_descriptor_t> _element_type;
225+ vm_array_t *_original;
226+ word_t _length;
227+ byte_t *_arr;
228+ };
229+
230+ // VM list type
231+ class vm_list_t final : public vm_alloc_t
232+ {
233+ public: //static
234+ static std::shared_ptr<const type_descriptor_t> type_desc();
235+
236+ public:
237+ vm_list_t(std::shared_ptr<const type_descriptor_t> td, vm_list_t *tail = nullptr);
238+ ~vm_list_t();
239+
240+ std::shared_ptr<const type_descriptor_t> get_element_type() const;
241+
242+ word_t get_length() const;
243+
244+ vm_list_t* get_tail() const;
245+
246+ vm_list_t* drop_tail();
247+
248+ // Returns a pointer to the value
249+ pointer_t value() const;
250+
251+ private:
252+ std::shared_ptr<const type_descriptor_t> _element_type;
253+ vm_list_t *_tail;
254+
255+ // Utilizing a union so that all value types can
256+ // be stored without allocating any extra memory.
257+ mutable union element_memory_t
258+ {
259+ pointer_t alloc;
260+ uint64_t local;
261+ } _mem;
262+
263+ const bool _is_alloc;
264+ };
265+
266+ // VM forward declation
267+ class vm_channel_t;
268+
269+ // VM request mutex
270+ struct vm_request_mutex_t
271+ {
272+ vm_request_mutex_t();
273+
274+ std::mutex pending_request_lock;
275+ bool pending_request;
276+ };
277+
278+ // VM channel request
279+ class vm_channel_request_t final
280+ {
281+ public:
282+ // Create a request using the ID of the requesting thread and an associated request mutex.
283+ vm_channel_request_t(uint32_t thread_id, vm_request_mutex_t &request_mutex);
284+
285+ uint32_t thread_id;
286+
287+ // Callback used to indicate the request was handled in the supplied channel.
288+ // This is only called in the event the request was not immediately serviced.
289+ std::function<void(const vm_channel_t &)> request_handled_callback;
290+
291+ // Data of the request
292+ pointer_t data;
293+
294+ std::reference_wrapper<vm_request_mutex_t> request_mutex;
295+ };
296+
297+ // VM channel type
298+ class vm_channel_t final : public vm_alloc_t
299+ {
300+ public: //static
301+ static std::shared_ptr<const type_descriptor_t> type_desc();
302+
303+ public:
304+ vm_channel_t(
305+ std::shared_ptr<const type_descriptor_t> td,
306+ std::function<void(pointer_t, pointer_t, const type_descriptor_t *)> transfer,
307+ word_t buffer_len);
308+ ~vm_channel_t();
309+
310+ std::shared_ptr<const type_descriptor_t> get_data_type() const;
311+
312+ // Send data into the channel.
313+ // Returns true if the data was sent, otherwise request is queued and false returned.
314+ bool send_data(vm_channel_request_t &send_request);
315+
316+ // Receive data from the channel.
317+ // Returns true if data was received, otherwise request is queued and false returned.
318+ bool receive_data(vm_channel_request_t &receive_request);
319+
320+ // Cancel an outstanding request from a thread.
321+ void cancel_request(uint32_t thread_id);
322+
323+ private:
324+ // Try to send data into the channel buffer.
325+ // Returns true if data was sent to buffer, otherwise false.
326+ bool try_send_to_buffer(vm_channel_request_t &send_request);
327+
328+ // Try to receive data from the channel buffer.
329+ // Returns true if data was received from buffer, otherwise false.
330+ bool try_receive_from_buffer(vm_channel_request_t &receive_request);
331+
332+ private:
333+ std::mutex _data_lock;
334+ std::shared_ptr<const type_descriptor_t> _data_type;
335+ std::function<void(pointer_t, pointer_t, const type_descriptor_t *)> _data_transfer;
336+
337+ vm_array_t *_data_buffer;
338+ uint32_t _data_buffer_head; // Next element to remove from buffer
339+ uint32_t _data_buffer_count; // Count of elements in buffer
340+
341+ using request_queue_t = std::deque<vm_channel_request_t>;
342+ request_queue_t _data_senders;
343+ request_queue_t _data_receivers;
344+ };
345+
346+ // Alt channel entry.
347+ static_assert(sizeof(word_t) == sizeof(pointer_t), "Alt channel stack layout will be invalid.");
348+ struct vm_alt_channel_stack_layout_t
349+ {
350+ word_t alloc;
351+ word_t data;
352+ };
353+
354+ // Alt channel layout as defined by the specification for the 'alt' instruction.
355+ struct vm_alt_stack_layout_t
356+ {
357+ word_t send_count;
358+ word_t receive_count;
359+ vm_alt_channel_stack_layout_t channels[1];
360+ };
361+
362+ // Runtime constants
363+ struct runtime_constants
364+ {
365+ static const auto nil = std::size_t{ 0 };
366+
367+ static const auto invalid_program_counter = vm_pc_t{ ~0 };
368+
369+ static const auto default_register_count = std::size_t{ 4 };
370+ static const auto default_register_size_bytes = sizeof(word_t);
371+
372+ // See VM frame definition
373+ static const auto fixed_point_register_1_frame_offset = std::size_t{ default_register_count * default_register_size_bytes };
374+ static const auto fixed_point_register_2_frame_offset = fixed_point_register_1_frame_offset + (2 * default_register_size_bytes);
375+ };
376+
377+ // Addressing mode for middle instruction data
378+ enum class address_mode_middle_t
379+ {
380+ none = 0,
381+ small_immediate = 1,
382+ small_offset_indirect_fp = 2,
383+ small_offset_indirect_mp = 3,
384+ };
385+
386+ // Middle instruction data
387+ struct middle_data_t
388+ {
389+ address_mode_middle_t mode;
390+ word_t register1;
391+ };
392+
393+ // Addressing mode for source and destination instruction data
394+ enum class address_mode_t
395+ {
396+ offset_indirect_mp = 0,
397+ offset_indirect_fp = 1,
398+ immediate = 2,
399+ none = 3,
400+ offset_double_indirect_mp = 4,
401+ offset_double_indirect_fp = 5,
402+ reserved_1 = 6,
403+ reserved_2 = 7,
404+ };
405+
406+ // Source and destination instruction data
407+ struct inst_data_generic_t
408+ {
409+ address_mode_t mode;
410+ word_t register1;
411+ word_t register2;
412+ };
413+
414+ using src_data_t = inst_data_generic_t;
415+ using dest_data_t = inst_data_generic_t;
416+
417+ // VM forward declation
418+ class vm_registers_t;
419+
420+ // Instruction execution operation
421+ using vm_exec_t = void(*)(vm_registers_t &, vm_t &);
422+
423+ // Opcode execution operation
424+ struct vm_exec_op_t
425+ {
426+ opcode_t opcode;
427+ src_data_t source;
428+ middle_data_t middle;
429+ dest_data_t destination;
430+ };
431+
432+ // VM Instruction (code section of module)
433+ union vm_instruction_t
434+ {
435+ vm_exec_op_t op;
436+ vm_exec_t native;
437+ };
438+
439+ // Flags that dictate the runtime operations of the module
440+ // [SPEC] Values past 'shared_module' are not fully documented.
441+ enum class runtime_flags_t
442+ {
443+ must_compile = 1 << 0,
444+ dont_compile = 1 << 1,
445+ share_module = 1 << 2, // [TODO] Respect this setting
446+ reserved_1 = 1 << 3, // DYNMOD
447+ has_import_deprecated = 1 << 4,
448+ has_handler = 1 << 5,
449+ has_import = 1 << 6,
450+
451+ builtin = 1 << 7,
452+ };
453+
454+ // Module header
455+ struct header_t
456+ {
457+ word_t magic_number;
458+ struct
459+ {
460+ word_t length;
461+ std::unique_ptr<byte_t[]> signature;
462+ } Signature;
463+
464+ runtime_flags_t runtime_flag;
465+ word_t stack_extent;
466+ word_t code_size;
467+ word_t data_size;
468+ word_t type_size;
469+ word_t export_size;
470+ vm_pc_t entry_pc;
471+ word_t entry_type; // Stack frame type
472+ };
473+
474+ // Export function entry
475+ struct export_function_t
476+ {
477+ export_function_t() = default;
478+ export_function_t(export_function_t &&);
479+
480+ export_function_t(const export_function_t &) = delete;
481+ export_function_t &operator=(const export_function_t &) = delete;
482+
483+ ~export_function_t();
484+
485+ vm_pc_t pc;
486+ word_t frame_type;
487+ word_t sig;
488+ std::unique_ptr<vm_string_t> name;
489+ };
490+
491+ // Import function entry
492+ struct import_function_t
493+ {
494+ import_function_t() = default;
495+ import_function_t(import_function_t &&);
496+
497+ import_function_t(const import_function_t &) = delete;
498+ import_function_t &operator=(const import_function_t &) = delete;
499+
500+ ~import_function_t();
501+
502+ word_t sig;
503+ std::unique_ptr<vm_string_t> name;
504+ };
505+
506+ // Import table for module
507+ struct import_vm_module_t
508+ {
509+ import_vm_module_t() = default;
510+ import_vm_module_t(import_vm_module_t &&);
511+
512+ import_vm_module_t(const import_vm_module_t &) = delete;
513+ import_vm_module_t &operator=(const import_vm_module_t &) = delete;
514+
515+ ~import_vm_module_t() = default;
516+
517+ std::vector<import_function_t> functions;
518+ };
519+
520+ // Exception entry for handler
521+ struct exception_t
522+ {
523+ exception_t() = default;
524+ exception_t(exception_t &&);
525+
526+ exception_t(const exception_t &) = delete;
527+ exception_t &operator=(const exception_t &) = delete;
528+
529+ ~exception_t();
530+
531+ vm_pc_t pc;
532+ std::unique_ptr<vm_string_t> name;
533+ };
534+
535+ // Exception handler
536+ struct handler_t
537+ {
538+ handler_t() = default;
539+ handler_t(handler_t &&);
540+
541+ handler_t(const handler_t &) = delete;
542+ handler_t &operator=(const handler_t &) = delete;
543+
544+ ~handler_t() = default;
545+
546+ // Byte offset in the handling frame the exception should be placed.
547+ word_t exception_offset;
548+ vm_pc_t begin_pc;
549+ vm_pc_t end_pc;
550+
551+ // Number of cases in exception table where the caught exception is of exception type.
552+ // See exceptions in Limbo language for details.
553+ word_t exception_type_count;
554+
555+ std::shared_ptr<const type_descriptor_t> type_desc;
556+ std::vector<exception_t> exception_table;
557+ };
558+
559+ using code_section_t = std::vector<vm_instruction_t>;
560+ using type_section_map_t = std::vector<std::shared_ptr<const type_descriptor_t>>;
561+ using export_section_t = std::unordered_multimap<word_t, const export_function_t>;
562+ using import_section_t = std::vector<import_vm_module_t>;
563+ using handler_section_t = std::vector<handler_t>;
564+
565+ // VM module
566+ struct vm_module_t
567+ {
568+ vm_module_t() = default;
569+ vm_module_t(const vm_module_t &) = delete;
570+ vm_module_t &operator=(const vm_module_t &) = delete;
571+
572+ ~vm_module_t();
573+
574+ // Contains a magic number, digital signature, runtime flags, sizes of the other sections, and a description of the entry point
575+ header_t header;
576+
577+ // Describes a sequence of instructions for the virtual machine
578+ code_section_t code_section;
579+
580+ // template module pointer
581+ std::unique_ptr<vm_alloc_t> original_mp;
582+
583+ // Type descriptors describing the layout of pointers within data types
584+ type_section_map_t type_section;
585+
586+ // Module name
587+ std::unique_ptr<vm_string_t> module_name;
588+
589+ // List of functions exported by this module
590+ export_section_t export_section;
591+
592+ // List of all functions imported from other modules
593+ import_section_t import_section;
594+
595+ // Lists all exception handlers declared in the module
596+ handler_section_t handler_section;
597+ };
598+
599+ // Reference to a function in an imported module.
600+ struct vm_module_function_ref_t
601+ {
602+ vm_pc_t entry_pc;
603+ word_t frame_type;
604+ };
605+
606+ // Reference to a module
607+ class vm_module_ref_t final : public vm_alloc_t
608+ {
609+ public: //static
610+ static std::shared_ptr<const type_descriptor_t> type_desc();
611+
612+ public:
613+ vm_module_ref_t(std::shared_ptr<const vm_module_t> module);
614+ vm_module_ref_t(std::shared_ptr<const vm_module_t> module, const import_vm_module_t &imports);
615+ ~vm_module_ref_t();
616+
617+ std::shared_ptr<const vm_module_t> module;
618+ vm_alloc_t *mp_base;
619+ const code_section_t &code_section;
620+ const type_section_map_t &type_section;
621+
622+ bool is_builtin_module() const;
623+ const vm_module_function_ref_t& get_function_ref(word_t index) const;
624+
625+ private:
626+ const bool _builtin_module;
627+ std::unique_ptr<vm_array_t> _function_refs;
628+ };
629+
630+ // VM stack frame
631+ class vm_frame_t final
632+ {
633+ public:
634+ vm_frame_t(std::shared_ptr<const type_descriptor_t> &frame_type);
635+ ~vm_frame_t();
636+
637+ std::shared_ptr<const type_descriptor_t> frame_type;
638+
639+ // Copy the frame contents
640+ void copy_frame_contents(const vm_frame_t &other);
641+
642+ // Pointer to the base of the frame.
643+ pointer_t base() const;
644+
645+ template<typename T>
646+ T& base() const
647+ {
648+ return *reinterpret_cast<T *>(base());
649+ }
650+
651+ vm_pc_t &prev_pc() const;
652+ vm_frame_t *&prev_frame() const;
653+
654+ // This is only set if this frame represents a module transition.
655+ vm_module_ref_t *&prev_module_ref() const;
656+
657+ // Fixed point register positions in frame
658+ // [SPEC] These are not defined in the vm spec.
659+
660+ // 'Stmp' in libinterp/xec.c
661+ pointer_t fixed_point_register_1() const;
662+
663+ // 'Dtmp' in libinterp/xec.c
664+ pointer_t fixed_point_register_2() const;
665+ };
666+
667+ // Callback for stack walk.
668+ // Returning 'false' during the stack walk will terminate the walk.
669+ // The second parameter is reserved for internal use by the VM and should not be manipulated.
670+ using vm_stack_walk_callback_t = std::function<bool(const vm_frame_t *f, const void *reserved)>;
671+
672+ // VM stack
673+ class vm_stack_t final
674+ {
675+ public:
676+ vm_stack_t(std::size_t stack_extent);
677+ ~vm_stack_t();
678+
679+ // Allocate a new frame instance.
680+ vm_frame_t *alloc_frame(std::shared_ptr<const type_descriptor_t> frame_type);
681+
682+ // Push the latest allocated frame as the new top frame.
683+ vm_frame_t *push_frame();
684+
685+ // Remove the top frame and return the next frame in the stack.
686+ vm_frame_t *pop_frame();
687+
688+ // Return the current top frame.
689+ vm_frame_t *peek_frame() const;
690+
691+ // Walk the stack getting a callback for each frame.
692+ // This function is __not__ thread safe.
693+ void walk_stack(vm_stack_walk_callback_t callback) const;
694+
695+ private:
696+ std::unique_ptr<vm_alloc_t> _mem;
697+ };
698+
699+ // Walk the supplied stack and faulting module find the handler, handling stack frame, and program counter for the supplied exception ID.
700+ // If an exception handler cannot be found an 'unhandled_user_exception' exception will be thrown.
701+ std::tuple<const handler_t *, vm_frame_t *, vm_pc_t> find_exception_handler(
702+ const vm_stack_t &stack,
703+ const vm_module_ref_t &faulting_module,
704+ const vm_pc_t faulting_pc,
705+ const vm_string_t &exception_id);
706+
707+ // VM thread states
708+ enum class vm_thread_state_t
709+ {
710+ unknown = 0,
711+
712+ blocked_in_alt, // blocked in alt instruction
713+ blocked_sending, // blocked waiting to send
714+ blocked_receiving, // blocked waiting to receive
715+
716+ debug, // thread is running under a debugger
717+ ready, // ready to run
718+ running,
719+
720+ release, // interpreter released - executing non-vm instructions
721+
722+ exiting, // exit instruction
723+ empty_stack,
724+
725+ broken, // thread crashed
726+ };
727+
728+ // Forward declaration
729+ class vm_thread_t;
730+
731+ // VM registers
732+ class vm_registers_t
733+ {
734+ public:
735+ vm_registers_t(
736+ const vm_thread_t &thread,
737+ vm_module_ref_t &entry);
738+ ~vm_registers_t();
739+
740+ const vm_thread_t &thread;
741+ vm_stack_t stack; // Frame pointer access (FP)
742+ vm_pc_t pc; // Program counter (Index into module code section)
743+ vm_alloc_t *mp_base; // Module data base pointer (MP)
744+ vm_module_ref_t *module_ref; // Module reference
745+ vm_request_mutex_t request_mutex;
746+ vm_thread_state_t current_thread_state;
747+ pointer_t src;
748+ pointer_t mid;
749+ pointer_t dest;
750+ };
751+
752+ // VM thread
753+ class vm_thread_t final : public vm_alloc_t
754+ {
755+ public: //static
756+ static std::shared_ptr<const type_descriptor_t> type_desc();
757+
758+ public:
759+ vm_thread_t(
760+ vm_module_ref_t &entry,
761+ uint32_t parent_thread_id);
762+ vm_thread_t(
763+ vm_module_ref_t &entry,
764+ const vm_frame_t &initial_frame,
765+ vm_pc_t start_pc,
766+ uint32_t parent_thread_id);
767+
768+ ~vm_thread_t();
769+
770+ vm_thread_state_t execute(vm_t &virtual_machine, uint32_t quanta);
771+
772+ vm_thread_state_t get_state() const;
773+
774+ uint32_t get_thread_id() const;
775+
776+ uint32_t get_parent_thread_id() const;
777+
778+ const vm_registers_t& get_registers() const;
779+
780+ private:
781+ const uint32_t _thread_id;
782+ const uint32_t _parent_thread_id;
783+ vm_registers_t _registers;
784+ };
785+
786+ //
787+ // VM interfaces
788+ //
789+
790+ // VM thread scheduler control interface
791+ class vm_scheduler_control_t
792+ {
793+ public:
794+ virtual ~vm_scheduler_control_t() = 0;
795+
796+ // Indicates the scheduler should enqueue the blocked thread with the supplied ID.
797+ // There is no indication if this function failed or succeeded.
798+ virtual void enqueue_blocked_thread(uint32_t thread_id) = 0;
799+
800+ // Get all VM threads
801+ virtual std::vector<std::shared_ptr<const vm_thread_t>> get_all_threads() const = 0;
802+ };
803+
804+ // VM thread scheduler interface
805+ class vm_scheduler_t
806+ {
807+ public:
808+ virtual ~vm_scheduler_t() = 0;
809+
810+ // Returns true if there are no vm threads executing, scheduled to be executed, or blocked.
811+ virtual bool is_idle() const = 0;
812+
813+ // Get controller
814+ virtual vm_scheduler_control_t &get_controller() const = 0;
815+
816+ // Schedule the supplied thread for execution
817+ virtual const vm_thread_t& schedule_thread(std::unique_ptr<vm_thread_t> thread) = 0;
818+ };
819+
820+ // VM garbage collector interface
821+ class vm_garbage_collector_t
822+ {
823+ public:
824+ // The supplied allocation is guaranteed to be valid for the lifetime of the callback.
825+ using vm_alloc_callback_t = std::function<void(const vm_alloc_t *)>;
826+
827+ public:
828+ virtual ~vm_garbage_collector_t() = 0;
829+
830+ // Supply an allocation for the collector to track.
831+ // Note that it is assumed the collector implementation will increment the reference count on the supplied alloc.
832+ virtual void track_allocation(vm_alloc_t *alloc) = 0;
833+
834+ // Enumerate allocations that are being tracked by the collector.
835+ // See callback type description.
836+ virtual void enum_tracked_allocations(vm_alloc_callback_t callback) const = 0;
837+
838+ // Called by the scheduler to inform the collector it should attempt to free resources.
839+ // All VM threads are guaranteed to be idle when called.
840+ // Returns true if the collection algorithm was run, otherwise false.
841+ virtual bool collect(std::vector<std::shared_ptr<const vm_thread_t>> &threads) = 0;
842+ };
843+ }
844+}
845+
846+#endif // _DISVM_SRC_INCLUDE_RUNTIME_H_
src/include/utf8.h+57−0
@@ -0,0 +1,57 @@
1+//
2+// Dis VM
3+// File: utf8.h
4+// Author: arr
5+//
6+
7+#ifndef _DISVM_SRC_INCLUDE_UTF8_H_
8+#define _DISVM_SRC_INCLUDE_UTF8_H_
9+
10+#include <cstdint>
11+#include "runtime.h"
12+
13+namespace disvm
14+{
15+ namespace runtime
16+ {
17+ namespace utf8
18+ {
19+ // As defined in http://tools.ietf.org/html/rfc3629
20+ struct constants
21+ {
22+ static const rune_t multi_byte_value_mask = 0x3f;
23+
24+ static const rune_t max_codepoint_ascii = 0x7f;
25+ static const rune_t max_codepoint_2_byte = 0x7ff;
26+ static const rune_t max_codepoint_3_byte = 0xffff;
27+ static const rune_t max_codepoint_4_byte = 0x10ffff;
28+
29+ static const rune_t max_supported_codepoint = max_codepoint_4_byte;
30+ };
31+
32+ // Compute the number of code points in the supplied string
33+ // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
34+ std::size_t count_codepoints(const uint8_t *str, const std::size_t max_len);
35+
36+ enum class decode_state
37+ {
38+ accept = 0,
39+ reject = 12
40+ };
41+
42+ // Decode a byte at a time to form a codepoint.
43+ // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
44+ decode_state decode_step(decode_state &state, rune_t &codepoint, uint8_t byte);
45+
46+ // Decode the next codepoint in the supplied string.
47+ // Returns number of characters in string consumed.
48+ std::size_t decode(const uint8_t *str, rune_t &codepoint);
49+
50+ // Encode the rune into the supplied buffer.
51+ // Returns updated pointer in the buffer or null on error.
52+ uint8_t *encode(const rune_t rune, uint8_t *str);
53+ }
54+ }
55+}
56+
57+#endif // _DISVM_SRC_INCLUDE_UTF8_H_
src/include/utils.h+47−0
@@ -0,0 +1,47 @@
1+//
2+// Dis VM
3+// File: utils.h
4+// Author: arr
5+//
6+
7+#ifndef _DISVM_SRC_INCLUDE_UTILS_H_
8+#define _DISVM_SRC_INCLUDE_UTILS_H_
9+
10+#include <cstdint>
11+#include <type_traits>
12+
13+namespace disvm
14+{
15+ namespace util
16+ {
17+ template<typename Flags>
18+ inline bool has_flag(const Flags flags, const Flags flag)
19+ {
20+ return (static_cast<std::underlying_type_t<Flags>>(flags) & static_cast<std::underlying_type_t<Flags>>(flag)) != 0;
21+ }
22+
23+ template <typename T1, typename T2>
24+ inline bool are_equal(const T1 &l, const T2 &r)
25+ {
26+ return (l == r);
27+ }
28+
29+ template <typename T1, typename T2, typename... Others>
30+ inline bool are_equal(const T1 &l, const T2 &r, const Others &... args)
31+ {
32+ return (l == r) && are_equal(r, args...);
33+ }
34+ }
35+}
36+
37+// Adopted from 'winnt.h' in the Windows SDK
38+#define DEFINE_ENUM_FLAG_OPERATORS(TYPE) \
39+inline TYPE operator | (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) | static_cast<std::underlying_type_t<TYPE>>(b)); } \
40+inline TYPE &operator |= (TYPE &a, TYPE b) { a = a | b; return a; } \
41+inline TYPE operator & (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) & static_cast<std::underlying_type_t<TYPE>>(b)); } \
42+inline TYPE &operator &= (TYPE &a, TYPE b) { a = a & b; return a; } \
43+inline TYPE operator ^ (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) ^ static_cast<std::underlying_type_t<TYPE>>(b)); } \
44+inline TYPE &operator ^= (TYPE &a, TYPE b) { a = a ^ b; return a; } \
45+inline TYPE operator ~ (TYPE a) { return static_cast<TYPE>(~static_cast<std::underlying_type_t<TYPE>>(a)); }
46+
47+#endif // _DISVM_SRC_INCLUDE_UTILS_H_
src/include/vm_memory.h+109−0
@@ -0,0 +1,109 @@
1+//
2+// Dis VM
3+// File: vm_memory.h
4+// Author: arr
5+//
6+
7+#ifndef _DISVM_SRC_INCLUDE_VM_MEMORY_H_
8+#define _DISVM_SRC_INCLUDE_VM_MEMORY_H_
9+
10+#include <cstdint>
11+#include <cassert>
12+#include <mutex>
13+#include <memory>
14+#include <bitset>
15+#include <functional>
16+#include "runtime.h"
17+
18+namespace disvm
19+{
20+ namespace runtime
21+ {
22+ class global_alloc_lock_t final
23+ {
24+ public:
25+ global_alloc_lock_t(const global_alloc_lock_t &) = delete;
26+ global_alloc_lock_t& operator=(const global_alloc_lock_t &) = delete;
27+
28+ global_alloc_lock_t(global_alloc_lock_t &&);
29+
30+ ~global_alloc_lock_t();
31+
32+ private:
33+ bool _owner;
34+ global_alloc_lock_t(bool owner);
35+ friend global_alloc_lock_t get_global_alloc_lock();
36+ };
37+
38+ // Take the global allocation lock for the VM heap.
39+ // While this lock is taken all further allocations will block.
40+ global_alloc_lock_t get_global_alloc_lock();
41+
42+ // Allocate memory from the VM heap
43+ void *alloc_memory(std::size_t amount_in_bytes);
44+
45+ template<typename T>
46+ T *alloc_memory(std::size_t amount_in_bytes = sizeof(T))
47+ {
48+ return reinterpret_cast<T *>(alloc_memory(amount_in_bytes));
49+ }
50+
51+ // Allocate memory from the VM heap and initialize to 0.
52+ void *calloc_memory(std::size_t amount_in_bytes);
53+
54+ template<typename T>
55+ T *calloc_memory(std::size_t amount_in_bytes = sizeof(T))
56+ {
57+ return reinterpret_cast<T *>(calloc_memory(amount_in_bytes));
58+ }
59+
60+ // Free memory on the VM heap
61+ void free_memory(void *memory);
62+
63+ // Initialize supplied memory based on the type descriptor
64+ void init_memory(const type_descriptor_t &type_desc, void *data);
65+
66+ // Destroy supplied memory based on the type descriptor
67+ void destroy_memory(const type_descriptor_t &type_desc, void *data);
68+
69+ // Increment the ref count of all points in the supplied memory allocation
70+ void inc_ref_count_in_memory(const type_descriptor_t &type_desc, void *data);
71+
72+ // Decrement the ref count and if 0 free the allocation.
73+ void dec_ref_count_and_free(vm_alloc_t *alloc);
74+
75+ using pointer_field_callback_t = std::function<void(pointer_t)>;
76+
77+ // The callback will be called on all non-null pointer fields.
78+ inline void enum_pointer_fields(const type_descriptor_t &type_desc, void *data, pointer_field_callback_t callback)
79+ {
80+ assert(data != nullptr && callback != nullptr);
81+ auto memory_word_len = std::size_t{ type_desc.size_in_bytes / sizeof(word_t) };
82+ if (memory_word_len == 0)
83+ return;
84+
85+ auto memory = reinterpret_cast<word_t *>(data);
86+ assert((memory_word_len / sizeof(word_t)) <= static_cast<std::size_t>(type_desc.size_in_bytes)); // type desc is in bytes convert len to bytes from words.
87+ for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8)
88+ {
89+ const auto words8 = type_desc.pointer_map[i];
90+ if (words8 != 0)
91+ {
92+ auto flags = std::bitset<sizeof(words8) * 8>{ words8 };
93+
94+ // Enumerating the flags in reverse order so memory access is linear.
95+ if (flags[7] && (memory[0] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[0]));
96+ if (flags[6] && (memory[1] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[1]));
97+ if (flags[5] && (memory[2] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[2]));
98+ if (flags[4] && (memory[3] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[3]));
99+ if (flags[3] && (memory[4] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[4]));
100+ if (flags[2] && (memory[5] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[5]));
101+ if (flags[1] && (memory[6] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[6]));
102+ if (flags[0] && (memory[7] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[7]));
103+ }
104+ }
105+ }
106+ }
107+}
108+
109+#endif // _DISVM_SRC_INCLUDE_VM_MEMORY_H_