Showing the changes to src/vm/vm_memory.cpp (1 of 40 files). Show all
Unified · Split
| @@ -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 | } | ||