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/vm/vm_memory.cpp (1 of 40 files). Show all

Unified · Split

src/vm/vm_memory.cpp+471−0
@@ -0,0 +1,471 @@
1//
2// Dis VM
3// File: vm_memory.cpp
4// Author: arr
5//
6
7#include <memory>
8#include <atomic>
9#include <bitset>
10#include <mutex>
11#include <condition_variable>
12#include <cinttypes>
13#include <vm_memory.h>
14#include <debug.h>
15#include <exceptions.h>
16
17using namespace disvm;
18using namespace disvm::runtime;
19
20namespace
21{
22 std::mutex global_lock;
23 std::condition_variable release_lock;
24
25 enum class lock_state_t
26 {
27 none,
28 pending,
29 active
30 };
31
32 std::atomic<uint32_t> allocating_thread_count;
33 std::atomic<lock_state_t> lock_state;
34
35 struct safe_alloc final
36 {
37 safe_alloc()
38 {
39 allocating_thread_count++;
40 if (lock_state != lock_state_t::none)
41 {
42 allocating_thread_count--;
43
44 std::unique_lock<std::mutex> lock{ global_lock };
45 release_lock.wait(lock, [&]{ return lock_state == lock_state_t::none; });
46
47 allocating_thread_count++;
48 }
49 }
50
51 ~safe_alloc()
52 {
53 assert(allocating_thread_count > 0);
54 allocating_thread_count--;
55 }
56 };
57}
58
59global_alloc_lock_t::global_alloc_lock_t(bool owner)
60 : _owner{ owner }
61{ }
62
63global_alloc_lock_t::global_alloc_lock_t(global_alloc_lock_t &&other)
64 : _owner{ other._owner }
65{
66 other._owner = false;
67}
68
69global_alloc_lock_t::~global_alloc_lock_t()
70{
71 if (_owner)
72 {
73 lock_state = lock_state_t::none;
74 release_lock.notify_all();
75 }
76}
77
78// Lock all allocations on the VM heap.
79global_alloc_lock_t disvm::runtime::get_global_alloc_lock()
80{
81 auto expected = lock_state_t::none;
82
83 // Block while trying to get lock
84 while (!lock_state.compare_exchange_strong(expected, lock_state_t::pending))
85 {
86 std::unique_lock<std::mutex> lock{ global_lock };
87 release_lock.wait(lock);
88 }
89
90 while (allocating_thread_count != 0)
91 {
92 // Spin-wait for all allocating threads to stop
93 }
94
95 // Transition to active lock state
96 lock_state = lock_state_t::active;
97
98 return{ true };
99}
100
101void *disvm::runtime::alloc_memory(std::size_t amount_in_bytes)
102{
103 safe_alloc s;
104
105 auto memory = std::malloc(amount_in_bytes);
106 if (memory == nullptr)
107 throw vm_system_exception{ "Out of memory" };
108
109 if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>())
110 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "alloc: %#" PRIxPTR " %d\n", memory, amount_in_bytes);
111
112 return memory;
113}
114
115void *disvm::runtime::calloc_memory(std::size_t amount_in_bytes)
116{
117 safe_alloc s;
118
119 auto memory = std::calloc(amount_in_bytes, sizeof(uint8_t));
120 if (memory == nullptr)
121 throw vm_system_exception{ "Out of memory" };
122
123 if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>())
124 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "alloc: zeroed: %#" PRIxPTR " %d\n", memory, amount_in_bytes);
125
126 return memory;
127}
128
129void disvm::runtime::free_memory(void *memory)
130{
131 if (memory == nullptr)
132 return;
133
134 if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>())
135 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "free: %#" PRIxPTR "\n", memory);
136
137 std::free(memory);
138}
139
140void disvm::runtime::init_memory(const type_descriptor_t &type_desc, void *data)
141{
142 assert(data != nullptr);
143 auto memory_word_len = std::size_t{ type_desc.size_in_bytes / sizeof(word_t) };
144 if (memory_word_len == 0)
145 return;
146
147 debug::assign_debug_memory(data, type_desc.size_in_bytes);
148
149 auto memory = reinterpret_cast<word_t *>(data);
150 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.
151 for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8)
152 {
153 const auto words8 = type_desc.pointer_map[i];
154 if (words8 != 0)
155 {
156 auto flags = std::bitset<sizeof(words8) * 8>{ words8 };
157
158 // Enumerating the flags in reverse order so memory access is linear.
159 if (flags[7]) memory[0] = runtime_constants::nil;
160 if (flags[6]) memory[1] = runtime_constants::nil;
161 if (flags[5]) memory[2] = runtime_constants::nil;
162 if (flags[4]) memory[3] = runtime_constants::nil;
163 if (flags[3]) memory[4] = runtime_constants::nil;
164 if (flags[2]) memory[5] = runtime_constants::nil;
165 if (flags[1]) memory[6] = runtime_constants::nil;
166 if (flags[0]) memory[7] = runtime_constants::nil;
167 }
168 }
169}
170
171namespace
172{
173 void dec_ref_and_free_pointer_field(pointer_t pointer_field)
174 {
175 assert(pointer_field != nullptr);
176 dec_ref_count_and_free(vm_alloc_t::from_allocation(pointer_field));
177 }
178
179 void add_ref_pointer_field(pointer_t pointer_field)
180 {
181 assert(pointer_field != nullptr);
182 vm_alloc_t::from_allocation(pointer_field)->add_ref();
183 }
184}
185
186void disvm::runtime::destroy_memory(const type_descriptor_t &type_desc, void *data)
187{
188 enum_pointer_fields(type_desc, data, dec_ref_and_free_pointer_field);
189
190 debug::assign_debug_memory(data, type_desc.size_in_bytes);
191}
192
193void disvm::runtime::inc_ref_count_in_memory(const type_descriptor_t &type_desc, void *data)
194{
195 if (data == nullptr)
196 return;
197
198 enum_pointer_fields(type_desc, data, add_ref_pointer_field);
199}
200
201void disvm::runtime::dec_ref_count_and_free(vm_alloc_t *alloc)
202{
203 if (alloc == nullptr)
204 return;
205
206 auto current_ref = alloc->release();
207 if (current_ref == 0)
208 delete alloc;
209}
210
211const zero_memory_t vm_alloc_t::zero_memory = {};
212
213void *vm_alloc_t::operator new(std::size_t sz)
214{
215 return alloc_memory(sz);
216}
217
218void vm_alloc_t::operator delete(void *ptr)
219{
220 free_memory(ptr);
221}
222
223vm_alloc_t *vm_alloc_t::allocate(std::shared_ptr<const type_descriptor_t> td)
224{
225 assert(td != nullptr);
226 if (td->size_in_bytes <= 0)
227 throw vm_system_exception{ "Invalid dynamic memory allocation size" };
228
229 auto mem = alloc_memory(sizeof(vm_alloc_t) + td->size_in_bytes);
230 auto alloc = ::new(mem)vm_alloc_t{ td };
231
232 // Initialize pointer types
233 init_memory(*td, alloc->allocation());
234
235 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm alloc: %d\n", td->size_in_bytes);
236 return alloc;
237}
238
239vm_alloc_t *vm_alloc_t::allocate(std::shared_ptr<const type_descriptor_t> td, const zero_memory_t &)
240{
241 assert(td != nullptr);
242 if (td->size_in_bytes <= 0)
243 throw vm_system_exception{ "Invalid dynamic memory allocation size" };
244
245 // No need to initialize memory since it is already zeroed out.
246 auto mem = calloc_memory(sizeof(vm_alloc_t) + td->size_in_bytes);
247 auto alloc = ::new(mem)vm_alloc_t{ td };
248
249 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "initz: vm alloc: %d\n", td->size_in_bytes);
250 return alloc;
251}
252
253vm_alloc_t *vm_alloc_t::copy(const vm_alloc_t &other)
254{
255 auto alloc_copy = vm_alloc_t::allocate(other.alloc_type);
256
257 // Copy the current memory contents of the allocation
258 std::memcpy(alloc_copy->allocation(), other.allocation(), alloc_copy->alloc_type->size_in_bytes);
259
260 // Ref count all dynamic allocations
261 inc_ref_count_in_memory(*alloc_copy->alloc_type, alloc_copy->allocation());
262
263 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "copy: vm alloc: %#" PRIxPTR " %#" PRIxPTR "\n", &other, alloc_copy);
264 return alloc_copy;
265}
266
267vm_alloc_t *vm_alloc_t::from_allocation(pointer_t allocation)
268{
269 if (allocation == nullptr)
270 return nullptr;
271
272 return reinterpret_cast<vm_alloc_t *>(reinterpret_cast<uint8_t *>(allocation) - sizeof(vm_alloc_t));
273}
274
275vm_alloc_t::vm_alloc_t(std::shared_ptr<const type_descriptor_t> td)
276 : alloc_type{ td }
277 , gc_reserved{ nullptr }
278 , _ref_count{ 1 }
279{
280 assert(alloc_type != nullptr);
281}
282
283vm_alloc_t::~vm_alloc_t()
284{
285 assert(_ref_count == 0);
286
287 auto finalizer = alloc_type->finalizer;
288 if (finalizer != type_descriptor_t::no_finalizer)
289 finalizer(this);
290
291 // Free pointer types
292 destroy_memory(*alloc_type, allocation());
293 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: vm alloc\n");
294}
295
296std::size_t vm_alloc_t::add_ref()
297{
298 assert(_ref_count > 0);
299
300 auto prev_value = _ref_count.fetch_add(1, std::memory_order::memory_order_relaxed);
301 return (prev_value + 1);
302}
303
304std::size_t vm_alloc_t::release()
305{
306 assert(_ref_count > 0);
307
308 auto prev_value = _ref_count.fetch_sub(1, std::memory_order::memory_order_relaxed);
309 return (prev_value - 1);
310}
311
312std::size_t vm_alloc_t::get_ref_count() const
313{
314 return _ref_count;
315}
316
317pointer_t vm_alloc_t::allocation() const
318{
319 // Remove const on the this pointer so the offset can be computed.
320 auto non_const_this = const_cast<vm_alloc_t *>(this);
321 return reinterpret_cast<pointer_t>(reinterpret_cast<uint8_t *>(non_const_this)+sizeof(vm_alloc_t));
322}
323
324namespace
325{
326 namespace hidden_type_desc
327 {
328 std::shared_ptr<const type_descriptor_t> byte;
329 std::shared_ptr<const type_descriptor_t> short_word;
330 std::shared_ptr<const type_descriptor_t> word;
331 std::shared_ptr<const type_descriptor_t> short_real;
332 std::shared_ptr<const type_descriptor_t> real;
333 std::shared_ptr<const type_descriptor_t> big;
334 std::shared_ptr<const type_descriptor_t> pointer;
335
336 std::shared_ptr<const type_descriptor_t> vm_array;
337 std::shared_ptr<const type_descriptor_t> vm_list;
338 std::shared_ptr<const type_descriptor_t> vm_channel;
339 std::shared_ptr<const type_descriptor_t> vm_string;
340
341 std::shared_ptr<const type_descriptor_t> vm_module_ref;
342 std::shared_ptr<const type_descriptor_t> vm_stack;
343 std::shared_ptr<const type_descriptor_t> vm_thread;
344 }
345}
346
347std::atomic_bool intrinsic_type_desc::is_initialized;
348
349void intrinsic_type_desc::initialize()
350{
351 auto is_false = bool{ false };
352 if (!intrinsic_type_desc::is_initialized.compare_exchange_strong(is_false, true))
353 return;
354
355 hidden_type_desc::byte = type_descriptor_t::create(sizeof(byte_t));
356 hidden_type_desc::short_word = type_descriptor_t::create(sizeof(short_word_t));
357 hidden_type_desc::word = type_descriptor_t::create(sizeof(word_t));
358 hidden_type_desc::short_real = type_descriptor_t::create(sizeof(short_real_t));
359 hidden_type_desc::real = type_descriptor_t::create(sizeof(real_t));
360 hidden_type_desc::big = type_descriptor_t::create(sizeof(big_t));
361 hidden_type_desc::pointer = type_descriptor_t::create(sizeof(pointer_t), std::vector<byte_t>{0x80});
362
363 hidden_type_desc::vm_array = type_descriptor_t::create(0);
364 hidden_type_desc::vm_list = type_descriptor_t::create(0);
365 hidden_type_desc::vm_channel = type_descriptor_t::create(0);
366 hidden_type_desc::vm_string = type_descriptor_t::create(0);
367
368 hidden_type_desc::vm_module_ref = type_descriptor_t::create(0);
369 hidden_type_desc::vm_stack = type_descriptor_t::create(0);
370 hidden_type_desc::vm_thread = type_descriptor_t::create(0);
371}
372
373template<>
374static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<byte_t>() { return hidden_type_desc::byte; }
375
376template<>
377static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<short_word_t>() { return hidden_type_desc::short_word; }
378
379template<>
380static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<word_t>() { return hidden_type_desc::word; }
381
382template<>
383static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<short_real_t>() { return hidden_type_desc::short_real; }
384
385template<>
386static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<real_t>() { return hidden_type_desc::real; }
387
388template<>
389static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<big_t>() { return hidden_type_desc::big; }
390
391template<>
392static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<pointer_t>() { return hidden_type_desc::pointer; }
393
394template<>
395static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_array_t>() { return hidden_type_desc::vm_array; }
396
397template<>
398static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_list_t>() { return hidden_type_desc::vm_list; }
399
400template<>
401static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_channel_t>() { return hidden_type_desc::vm_channel; }
402
403template<>
404static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_string_t>() { return hidden_type_desc::vm_string; }
405
406template<>
407static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_module_ref_t>() { return hidden_type_desc::vm_module_ref; }
408
409template<>
410static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_stack_t>() { return hidden_type_desc::vm_stack; }
411
412template<>
413static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_thread_t>() { return hidden_type_desc::vm_thread; }
414
415vm_alloc_instance_finalizer_t type_descriptor_t::no_finalizer = nullptr;
416
417std::shared_ptr<const type_descriptor_t> type_descriptor_t::create(const word_t size)
418{
419 return type_descriptor_t::create(size, 0, nullptr);
420}
421
422std::shared_ptr<const type_descriptor_t> type_descriptor_t::create(const word_t size, const std::vector<byte_t> &pointer_map)
423{
424 assert(pointer_map.size() < static_cast<std::size_t>(std::numeric_limits<word_t>::max()));
425 return type_descriptor_t::create(size, static_cast<word_t>(pointer_map.size()), pointer_map.data());
426}
427
428std::shared_ptr<const type_descriptor_t> type_descriptor_t::create(
429 const word_t size,
430 const word_t pointer_map_length,
431 const byte_t *pointer_map,
432 const vm_alloc_instance_finalizer_t finalizer)
433{
434 struct
435 {
436 void operator()(type_descriptor_t *td)
437 {
438 free_memory(const_cast<byte_t *>(td->pointer_map));
439 debug::assign_debug_pointer(const_cast<byte_t **>(&td->pointer_map));
440
441 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: type descriptor\n");
442 free_memory(td);
443 }
444 } deleter;
445
446 auto new_type_memory = alloc_memory(sizeof(type_descriptor_t));
447
448 byte_t *pointer_map_local = nullptr;
449 if (pointer_map_length > 0)
450 {
451 pointer_map_local = alloc_memory<byte_t>(pointer_map_length);
452 for (auto i = word_t{ 0 }; i < pointer_map_length; ++i)
453 pointer_map_local[i] = pointer_map[i];
454 }
455
456 auto new_type = ::new(new_type_memory) type_descriptor_t{ size, pointer_map_length, pointer_map_local, finalizer };
457 return std::shared_ptr<type_descriptor_t>{ new_type, deleter };
458}
459
460type_descriptor_t::type_descriptor_t(
461 word_t size_in_bytes,
462 word_t map_in_bytes,
463 const byte_t * pointer_map,
464 vm_alloc_instance_finalizer_t finalizer)
465 : size_in_bytes{ size_in_bytes }
466 , map_in_bytes{ map_in_bytes }
467 , pointer_map{ pointer_map }
468 , finalizer{ finalizer }
469{
470 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: type descriptor\n");
471}