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
17namespace 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
14namespace 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
17namespace 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
15namespace 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
13namespace 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
12namespace 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
22namespace 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
13namespace 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
13namespace 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) \
39inline 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)); } \
40inline TYPE &operator |= (TYPE &a, TYPE b) { a = a | b; return a; } \
41inline 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)); } \
42inline TYPE &operator &= (TYPE &a, TYPE b) { a = a & b; return a; } \
43inline 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)); } \
44inline TYPE &operator ^= (TYPE &a, TYPE b) { a = a ^ b; return a; } \
45inline 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
18namespace 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_