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+
17+using namespace disvm;
18+using namespace disvm::runtime;
19+
20+namespace
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+
59+global_alloc_lock_t::global_alloc_lock_t(bool owner)
60+ : _owner{ owner }
61+{ }
62+
63+global_alloc_lock_t::global_alloc_lock_t(global_alloc_lock_t &&other)
64+ : _owner{ other._owner }
65+{
66+ other._owner = false;
67+}
68+
69+global_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.
79+global_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+
101+void *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+
115+void *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+
129+void 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+
140+void 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+
171+namespace
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+
186+void 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+
193+void 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+
201+void 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+
211+const zero_memory_t vm_alloc_t::zero_memory = {};
212+
213+void *vm_alloc_t::operator new(std::size_t sz)
214+{
215+ return alloc_memory(sz);
216+}
217+
218+void vm_alloc_t::operator delete(void *ptr)
219+{
220+ free_memory(ptr);
221+}
222+
223+vm_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+
239+vm_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+
253+vm_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+
267+vm_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+
275+vm_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+
283+vm_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+
296+std::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+
304+std::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+
312+std::size_t vm_alloc_t::get_ref_count() const
313+{
314+ return _ref_count;
315+}
316+
317+pointer_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+
324+namespace
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+
347+std::atomic_bool intrinsic_type_desc::is_initialized;
348+
349+void 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+
373+template<>
374+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<byte_t>() { return hidden_type_desc::byte; }
375+
376+template<>
377+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<short_word_t>() { return hidden_type_desc::short_word; }
378+
379+template<>
380+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<word_t>() { return hidden_type_desc::word; }
381+
382+template<>
383+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<short_real_t>() { return hidden_type_desc::short_real; }
384+
385+template<>
386+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<real_t>() { return hidden_type_desc::real; }
387+
388+template<>
389+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<big_t>() { return hidden_type_desc::big; }
390+
391+template<>
392+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<pointer_t>() { return hidden_type_desc::pointer; }
393+
394+template<>
395+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_array_t>() { return hidden_type_desc::vm_array; }
396+
397+template<>
398+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_list_t>() { return hidden_type_desc::vm_list; }
399+
400+template<>
401+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_channel_t>() { return hidden_type_desc::vm_channel; }
402+
403+template<>
404+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_string_t>() { return hidden_type_desc::vm_string; }
405+
406+template<>
407+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_module_ref_t>() { return hidden_type_desc::vm_module_ref; }
408+
409+template<>
410+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_stack_t>() { return hidden_type_desc::vm_stack; }
411+
412+template<>
413+static std::shared_ptr<const type_descriptor_t> intrinsic_type_desc::type<vm_thread_t>() { return hidden_type_desc::vm_thread; }
414+
415+vm_alloc_instance_finalizer_t type_descriptor_t::no_finalizer = nullptr;
416+
417+std::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+
422+std::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+
428+std::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+
460+type_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+}