Floating point division is now relying on underlying FPU and IEEE for behavior with respect to divide by 0
Unified · Split
| @@ -91,7 +91,7 @@ namespace · 90 unmodified lines | |||
| 91 | 91 | ||
| 92 | // Initialize the module base register | 92 | // Initialize the module base register |
| 93 | const auto mp_type = type_descriptor_t::create(8, { 0xc0 }); | 93 | const auto mp_type = type_descriptor_t::create(8, { 0xc0 }); |
| 94 | e.original_mp.reset(vm_alloc_t::allocate(mp_type, vm_alloc_t::zero_memory)); | 94 | e.original_mp.reset(vm_alloc_t::allocate(mp_type)); |
| 95 | auto mp_base = e.original_mp->get_allocation<pointer_t>(); | 95 | auto mp_base = e.original_mp->get_allocation<pointer_t>(); |
| 96 | 96 | ||
| 97 | // Take a reference on the command module path and store it on the entry frame | 97 | // Take a reference on the command module path and store it on the entry frame |
| @@ -91,7 +91,7 @@ namespace disvm · 90 unmodified lines | |||
| 91 | { | 91 | { |
| 92 | #ifndef NDEBUG | 92 | #ifndef NDEBUG |
| 93 | assert(t != nullptr); | 93 | assert(t != nullptr); |
| 94 | *t = reinterpret_cast<T *>(0xbada110c); | 94 | assign_debug_memory(t, sizeof(t)); |
| 95 | #endif | 95 | #endif |
| 96 | } | 96 | } |
| 97 | } | 97 | } |
| @@ -89,21 +89,14 @@ namespace disvm · 88 unmodified lines | |||
| 89 | static std::shared_ptr<const type_descriptor_t> type(); | 89 | static std::shared_ptr<const type_descriptor_t> type(); |
| 90 | }; | 90 | }; |
| 91 | 91 | ||
| 92 | // Type used to indicate allocated memory should be zeroed out. | ||
| 93 | struct zero_memory_t | ||
| 94 | { }; | ||
| 95 | |||
| 96 | // VM memory allocation | 92 | // VM memory allocation |
| 97 | class vm_alloc_t | 93 | class vm_alloc_t |
| 98 | { | 94 | { |
| 99 | public: // static | 95 | public: // static |
| 100 | static const zero_memory_t zero_memory; | ||
| 101 | |||
| 102 | static void *operator new(std::size_t sz); | 96 | static void *operator new(std::size_t sz); |
| 103 | static void operator delete(void *ptr); | 97 | static void operator delete(void *ptr); |
| 104 | 98 | ||
| 105 | static vm_alloc_t *allocate(std::shared_ptr<const type_descriptor_t> td); | 99 | static vm_alloc_t *allocate(std::shared_ptr<const type_descriptor_t> td); |
| 106 | static vm_alloc_t *allocate(std::shared_ptr<const type_descriptor_t> td, const zero_memory_t &); | ||
| 107 | static vm_alloc_t *copy(const vm_alloc_t &other); | 100 | static vm_alloc_t *copy(const vm_alloc_t &other); |
| 108 | static vm_alloc_t *from_allocation(pointer_t allocation); | 101 | static vm_alloc_t *from_allocation(pointer_t allocation); |
| 109 | 102 | ||
| @@ -205,7 +198,6 @@ namespace disvm · 95 unmodified lines | |||
| 205 | 198 | ||
| 206 | public: | 199 | public: |
| 207 | vm_array_t(std::shared_ptr<const type_descriptor_t> td, word_t length); | 200 | vm_array_t(std::shared_ptr<const type_descriptor_t> td, word_t length); |
| 208 | vm_array_t(std::shared_ptr<const type_descriptor_t> td, word_t length, const zero_memory_t&); | ||
| 209 | vm_array_t(vm_array_t *original, word_t begin_index, word_t length); | 201 | vm_array_t(vm_array_t *original, word_t begin_index, word_t length); |
| 210 | vm_array_t(const vm_string_t *s); | 202 | vm_array_t(const vm_string_t *s); |
| 211 | ~vm_array_t(); | 203 | ~vm_array_t(); |
| @@ -40,7 +40,9 @@ namespace disvm · 39 unmodified lines | |||
| 40 | // While this lock is taken all further allocations will block. | 40 | // While this lock is taken all further allocations will block. |
| 41 | global_alloc_lock_t get_global_alloc_lock(); | 41 | global_alloc_lock_t get_global_alloc_lock(); |
| 42 | 42 | ||
| 43 | // Allocate memory from the VM heap | 43 | // Allocate memory from the VM heap that is initialize to 0. |
| 44 | // [SPEC] All memory in this implementation of the Dis VM spec | ||
| 45 | // will be 0 initialized. | ||
| 44 | void *alloc_memory(std::size_t amount_in_bytes); | 46 | void *alloc_memory(std::size_t amount_in_bytes); |
| 45 | 47 | ||
| 46 | template<typename T> | 48 | template<typename T> |
| @@ -49,15 +51,6 @@ namespace disvm · 2 unmodified lines | |||
| 49 | return reinterpret_cast<T *>(alloc_memory(amount_in_bytes)); | 51 | return reinterpret_cast<T *>(alloc_memory(amount_in_bytes)); |
| 50 | } | 52 | } |
| 51 | 53 | ||
| 52 | // Allocate memory from the VM heap and initialize to 0. | ||
| 53 | void *calloc_memory(std::size_t amount_in_bytes); | ||
| 54 | |||
| 55 | template<typename T> | ||
| 56 | T *calloc_memory(std::size_t amount_in_bytes = sizeof(T)) | ||
| 57 | { | ||
| 58 | return reinterpret_cast<T *>(calloc_memory(amount_in_bytes)); | ||
| 59 | } | ||
| 60 | |||
| 61 | // Free memory on the VM heap | 54 | // Free memory on the VM heap |
| 62 | void free_memory(void *memory); | 55 | void free_memory(void *memory); |
| 63 | 56 | ||
| @@ -43,19 +43,6 @@ vm_array_t::vm_array_t(std::shared_ptr<const type_descriptor_t> td, word_t lengt · 42 unmodified lines | |||
| 43 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "end: init: elements: %d", array_len); | 43 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "end: init: elements: %d", array_len); |
| 44 | } | 44 | } |
| 45 | 45 | ||
| 46 | vm_array_t::vm_array_t(std::shared_ptr<const type_descriptor_t> td, word_t length, const zero_memory_t&) | ||
| 47 | : vm_alloc_t(vm_array_t::type_desc()) | ||
| 48 | , _arr{ nullptr } | ||
| 49 | , _element_type{ td } | ||
| 50 | , _length{ length } | ||
| 51 | , _original{ nullptr } | ||
| 52 | { | ||
| 53 | assert(_element_type != nullptr); | ||
| 54 | assert(_length >= 0); | ||
| 55 | _arr = calloc_memory<byte_t>(_length * _element_type->size_in_bytes); | ||
| 56 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "initz: vm array: %d", _length); | ||
| 57 | } | ||
| 58 | |||
| 59 | vm_array_t::vm_array_t(vm_array_t *original, word_t begin_index, word_t length) | 46 | vm_array_t::vm_array_t(vm_array_t *original, word_t begin_index, word_t length) |
| 60 | : vm_alloc_t(vm_array_t::type_desc()) | 47 | : vm_alloc_t(vm_array_t::type_desc()) |
| 61 | , _arr{ nullptr } | 48 | , _arr{ nullptr } |
| @@ -294,6 +294,16 @@ namespace · 293 unmodified lines | |||
| 294 | vt_ref<PrimitiveType>(r.dest) = vt_ref<PrimitiveType>(r.mid) / denom; | 294 | vt_ref<PrimitiveType>(r.dest) = vt_ref<PrimitiveType>(r.mid) / denom; |
| 295 | } | 295 | } |
| 296 | 296 | ||
| 297 | // [SPEC] The C++ defines division by 0 as undefined, however the IEEE standard does | ||
| 298 | // define the behavior. Therefore the behavior is platform dependent and controlled | ||
| 299 | // by the built-in Math module. | ||
| 300 | template<> | ||
| 301 | void _div<real_t>(vm_registers_t &r) | ||
| 302 | { | ||
| 303 | const auto denom = vt_ref<real_t>(r.src); | ||
| 304 | vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.mid) / denom; | ||
| 305 | } | ||
| 306 | |||
| 297 | EXEC_DECL(divb) { _div<byte_t>(r); } | 307 | EXEC_DECL(divb) { _div<byte_t>(r); } |
| 298 | EXEC_DECL(divw) { _div<word_t>(r); } | 308 | EXEC_DECL(divw) { _div<word_t>(r); } |
| 299 | EXEC_DECL(divf) { _div<real_t>(r); } | 309 | EXEC_DECL(divf) { _div<real_t>(r); } |
| @@ -1318,7 +1328,7 @@ namespace · 1018 unmodified lines | |||
| 1318 | 1328 | ||
| 1319 | namespace | 1329 | namespace |
| 1320 | { | 1330 | { |
| 1321 | void new_instance(vm_registers_t &r, const vm_t &vm, const bool zero_memory) | 1331 | void new_instance(vm_registers_t &r, const vm_t &vm) |
| 1322 | { | 1332 | { |
| 1323 | const auto type_id = vt_ref<word_t>(r.src); | 1333 | const auto type_id = vt_ref<word_t>(r.src); |
| 1324 | const auto &types = r.module_ref->type_section; | 1334 | const auto &types = r.module_ref->type_section; |
| @@ -1327,7 +1337,7 @@ namespace · 2 unmodified lines | |||
| 1327 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); | 1337 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 1328 | 1338 | ||
| 1329 | auto type_desc = types[type_id]; | 1339 | auto type_desc = types[type_id]; |
| 1330 | auto new_alloc = !zero_memory ? vm_alloc_t::allocate(type_desc) : vm_alloc_t::allocate(type_desc, vm_alloc_t::zero_memory); | 1340 | auto new_alloc = vm_alloc_t::allocate(type_desc); |
| 1331 | 1341 | ||
| 1332 | if (type_desc->map_in_bytes > 0) | 1342 | if (type_desc->map_in_bytes > 0) |
| 1333 | vm.get_garbage_collector().track_allocation(new_alloc); | 1343 | vm.get_garbage_collector().track_allocation(new_alloc); |
| @@ -1338,12 +1348,14 @@ namespace · 4 unmodified lines | |||
| 1338 | 1348 | ||
| 1339 | EXEC_DECL(new_) | 1349 | EXEC_DECL(new_) |
| 1340 | { | 1350 | { |
| 1341 | new_instance(r, vm, false /* zero memory */); | 1351 | new_instance(r, vm); |
| 1342 | } | 1352 | } |
| 1343 | 1353 | ||
| 1344 | EXEC_DECL(newz) | 1354 | EXEC_DECL(newz) |
| 1345 | { | 1355 | { |
| 1346 | new_instance(r, vm, true /* zero memory */); | 1356 | // See allocation implementation for guarantee about |
| 1357 | // always using zeroed memory. | ||
| 1358 | new_instance(r, vm); | ||
| 1347 | } | 1359 | } |
| 1348 | 1360 | ||
| 1349 | EXEC_DECL(mnewz) | 1361 | EXEC_DECL(mnewz) |
| @@ -1359,7 +1371,7 @@ namespace · 9 unmodified lines | |||
| 1359 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); | 1371 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 1360 | 1372 | ||
| 1361 | auto type_desc = types[type_id]; | 1373 | auto type_desc = types[type_id]; |
| 1362 | auto new_alloc = vm_alloc_t::allocate(type_desc, vm_alloc_t::zero_memory); | 1374 | auto new_alloc = vm_alloc_t::allocate(type_desc); |
| 1363 | 1375 | ||
| 1364 | if (type_desc->map_in_bytes > 0) | 1376 | if (type_desc->map_in_bytes > 0) |
| 1365 | vm.get_garbage_collector().track_allocation(new_alloc); | 1377 | vm.get_garbage_collector().track_allocation(new_alloc); |
| @@ -1369,7 +1381,7 @@ namespace · 3 unmodified lines | |||
| 1369 | 1381 | ||
| 1370 | namespace | 1382 | namespace |
| 1371 | { | 1383 | { |
| 1372 | void new_array(vm_registers_t &r, const vm_t &vm, const bool zero_memory) | 1384 | void new_array(vm_registers_t &r, const vm_t &vm) |
| 1373 | { | 1385 | { |
| 1374 | assert(r.module_ref != nullptr); | 1386 | assert(r.module_ref != nullptr); |
| 1375 | const auto &types = r.module_ref->type_section; | 1387 | const auto &types = r.module_ref->type_section; |
| @@ -1381,7 +1393,7 @@ namespace · 5 unmodified lines | |||
| 1381 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); | 1393 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 1382 | 1394 | ||
| 1383 | const auto array_size = vt_ref<word_t>(r.src); | 1395 | const auto array_size = vt_ref<word_t>(r.src); |
| 1384 | auto new_array = !zero_memory ? new vm_array_t(type_desc, array_size) : new vm_array_t(type_desc, array_size, vm_alloc_t::zero_memory); | 1396 | auto new_array = new vm_array_t(type_desc, array_size); |
| 1385 | 1397 | ||
| 1386 | if (type_desc->map_in_bytes > 0) | 1398 | if (type_desc->map_in_bytes > 0) |
| 1387 | vm.get_garbage_collector().track_allocation(new_array); | 1399 | vm.get_garbage_collector().track_allocation(new_array); |
| @@ -1392,12 +1404,14 @@ namespace · 4 unmodified lines | |||
| 1392 | 1404 | ||
| 1393 | EXEC_DECL(newa) | 1405 | EXEC_DECL(newa) |
| 1394 | { | 1406 | { |
| 1395 | new_array(r, vm, false /* zero memory */); | 1407 | new_array(r, vm); |
| 1396 | } | 1408 | } |
| 1397 | 1409 | ||
| 1398 | EXEC_DECL(newaz) | 1410 | EXEC_DECL(newaz) |
| 1399 | { | 1411 | { |
| 1400 | new_array(r, vm, true /* zero memory */); | 1412 | // See allocation implementation for guarantee about |
| 1413 | // always using zeroed memory. | ||
| 1414 | new_array(r, vm); | ||
| 1401 | } | 1415 | } |
| 1402 | 1416 | ||
| 1403 | EXEC_DECL(tcmp) | 1417 | EXEC_DECL(tcmp) |
| @@ -362,10 +362,7 @@ namespace · 361 unmodified lines | |||
| 362 | { | 362 | { |
| 363 | auto vm_module_type = modobj.type_section[module_constants::vm_module_type_desc_number]; | 363 | auto vm_module_type = modobj.type_section[module_constants::vm_module_type_desc_number]; |
| 364 | if (vm_module_type->size_in_bytes != modobj.header.data_size) throw module_reader_exception{ "Invalid type desc for MP" }; | 364 | if (vm_module_type->size_in_bytes != modobj.header.data_size) throw module_reader_exception{ "Invalid type desc for MP" }; |
| 365 | 365 | modobj.original_mp.reset(vm_alloc_t::allocate(vm_module_type)); | |
| 366 | // [SPEC] The Dis VM spec does not guarantee the MP segment (i.e. data segment) of the loaded | ||
| 367 | // module will be initialized to zero, but it is the C/C++ approach so defer to that. | ||
| 368 | modobj.original_mp.reset(vm_alloc_t::allocate(vm_module_type, vm_alloc_t::zero_memory)); | ||
| 369 | } | 366 | } |
| 370 | 367 | ||
| 371 | auto array_stack = std::array<byte_t *, module_constants::array_address_stack_size>{}; | 368 | auto array_stack = std::array<byte_t *, module_constants::array_address_stack_size>{}; |
| @@ -66,7 +66,7 @@ namespace · 65 unmodified lines | |||
| 66 | void *new_page() | 66 | void *new_page() |
| 67 | { | 67 | { |
| 68 | auto current_stack_page = top_page; | 68 | auto current_stack_page = top_page; |
| 69 | top_page = calloc_memory<vm_stack_page>(sizeof(vm_stack_page) + stack_page_size); | 69 | top_page = alloc_memory<vm_stack_page>(sizeof(vm_stack_page) + stack_page_size); |
| 70 | 70 | ||
| 71 | // Initialize the stack allocation. | 71 | // Initialize the stack allocation. |
| 72 | top_page->prev_page = current_stack_page; | 72 | top_page->prev_page = current_stack_page; |
| @@ -217,7 +217,7 @@ vm_string_t::vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str · 216 unmodified lines | |||
| 217 | { | 217 | { |
| 218 | // If we are going to allocate, make sure it is larger than needed. | 218 | // If we are going to allocate, make sure it is larger than needed. |
| 219 | _length_max = compute_max_length(_length); | 219 | _length_max = compute_max_length(_length); |
| 220 | _mem.alloc = calloc_memory<uint8_t>(_length_max * _character_size); | 220 | _mem.alloc = alloc_memory<uint8_t>(_length_max * _character_size); |
| 221 | _is_alloc = true; | 221 | _is_alloc = true; |
| 222 | 222 | ||
| 223 | dest = _mem.alloc; | 223 | dest = _mem.alloc; |
| @@ -248,7 +248,7 @@ vm_string_t::vm_string_t(const vm_string_t &s1, const vm_string_t &s2) · 24 unmodified lines | |||
| 248 | , _length_max{ compute_max_length(s1._length + s2._length) } | 248 | , _length_max{ compute_max_length(s1._length + s2._length) } |
| 249 | , _mem{} | 249 | , _mem{} |
| 250 | { | 250 | { |
| 251 | _mem.alloc = calloc_memory<uint8_t>(_length_max * _character_size); | 251 | _mem.alloc = alloc_memory<uint8_t>(_length_max * _character_size); |
| 252 | 252 | ||
| 253 | const auto s1_data = (s1._is_alloc) ? s1._mem.alloc : s1._mem.local; | 253 | const auto s1_data = (s1._is_alloc) ? s1._mem.alloc : s1._mem.local; |
| 254 | const auto s2_data = (s2._is_alloc) ? s2._mem.alloc : s2._mem.local; | 254 | const auto s2_data = (s2._is_alloc) ? s2._mem.alloc : s2._mem.local; |
| @@ -290,7 +290,7 @@ vm_string_t::vm_string_t(const vm_string_t &other, word_t begin_index, word_t en · 35 unmodified lines | |||
| 290 | if (_is_alloc) | 290 | if (_is_alloc) |
| 291 | { | 291 | { |
| 292 | src = other._mem.alloc; | 292 | src = other._mem.alloc; |
| 293 | _mem.alloc = calloc_memory<uint8_t>(_length_max); | 293 | _mem.alloc = alloc_memory<uint8_t>(_length_max); |
| 294 | dest = _mem.alloc; | 294 | dest = _mem.alloc; |
| 295 | } | 295 | } |
| 296 | 296 | ||
| @@ -358,9 +358,9 @@ vm_string_t &vm_string_t::append(const vm_string_t &other) · 61 unmodified lines | |||
| 358 | 358 | ||
| 359 | // Check if this string and the supplied string have the same character size. | 359 | // Check if this string and the supplied string have the same character size. |
| 360 | if (new_is_rune) | 360 | if (new_is_rune) |
| 361 | new_source = calloc_memory<uint8_t>(new_length_max * sizeof(rune_t)); | 361 | new_source = alloc_memory<uint8_t>(new_length_max * sizeof(rune_t)); |
| 362 | else | 362 | else |
| 363 | new_source = calloc_memory<uint8_t>(new_length_max); | 363 | new_source = alloc_memory<uint8_t>(new_length_max); |
| 364 | 364 | ||
| 365 | // Combine the two string sources | 365 | // Combine the two string sources |
| 366 | combine( | 366 | combine( |
| @@ -449,7 +449,7 @@ void vm_string_t::set_rune(word_t index, rune_t value) · 82 unmodified lines | |||
| 449 | assert(_character_size == sizeof(rune_t)); | 449 | assert(_character_size == sizeof(rune_t)); |
| 450 | 450 | ||
| 451 | _length_max = (_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(_length); | 451 | _length_max = (_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(_length); |
| 452 | auto new_alloc = calloc_memory<rune_t>(_length_max * _character_size); | 452 | auto new_alloc = alloc_memory<rune_t>(_length_max * _character_size); |
| 453 | 453 | ||
| 454 | // Copy source into the new alloc. | 454 | // Copy source into the new alloc. |
| 455 | copy_characters_to(new_alloc, 0, source, _length); | 455 | copy_characters_to(new_alloc, 0, source, _length); |
| @@ -467,7 +467,7 @@ void vm_string_t::set_rune(word_t index, rune_t value) · 11 unmodified lines | |||
| 467 | else if (static_cast<word_t>(_length_max) <= index) // Check if string can accomodate the new character. | 467 | else if (static_cast<word_t>(_length_max) <= index) // Check if string can accomodate the new character. |
| 468 | { | 468 | { |
| 469 | _length_max = (_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(_length); | 469 | _length_max = (_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(_length); |
| 470 | auto new_alloc = calloc_memory<uint8_t>(_length_max * _character_size); | 470 | auto new_alloc = alloc_memory<uint8_t>(_length_max * _character_size); |
| 471 | std::memcpy(new_alloc, source, _length * _character_size); | 471 | std::memcpy(new_alloc, source, _length * _character_size); |
| 472 | 472 | ||
| 473 | // Free memory, if allocated | 473 | // Free memory, if allocated |
| @@ -255,7 +255,7 @@ vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) · 254 unmodified lines | |||
| 255 | } | 255 | } |
| 256 | catch (const unhandled_user_exception &uue) | 256 | catch (const unhandled_user_exception &uue) |
| 257 | { | 257 | { |
| 258 | _error_message = calloc_memory<char>(max_error_message); | 258 | _error_message = alloc_memory<char>(max_error_message); |
| 259 | const int ec = std::snprintf(_error_message, max_error_message, "%s in %s @%d\n %s", uue.what(), uue.module_name, uue.program_counter, uue.exception_id); | 259 | const int ec = std::snprintf(_error_message, max_error_message, "%s in %s @%d\n %s", uue.what(), uue.module_name, uue.program_counter, uue.exception_id); |
| 260 | assert(ec > 0 && ec < max_error_message); | 260 | assert(ec > 0 && ec < max_error_message); |
| 261 | (void)ec; | 261 | (void)ec; |
| @@ -263,7 +263,7 @@ vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) · 1 unmodified line | |||
| 263 | } | 263 | } |
| 264 | catch (const index_out_of_range_memory &ioor) | 264 | catch (const index_out_of_range_memory &ioor) |
| 265 | { | 265 | { |
| 266 | _error_message = calloc_memory<char>(max_error_message); | 266 | _error_message = alloc_memory<char>(max_error_message); |
| 267 | const int ec = std::snprintf(_error_message, max_error_message, "%s - %d [%d,%d]", ioor.what(), ioor.invalid_value, ioor.valid_min, ioor.valid_max); | 267 | const int ec = std::snprintf(_error_message, max_error_message, "%s - %d [%d,%d]", ioor.what(), ioor.invalid_value, ioor.valid_min, ioor.valid_max); |
| 268 | assert(ec > 0 && ec < max_error_message); | 268 | assert(ec > 0 && ec < max_error_message); |
| 269 | (void)ec; | 269 | (void)ec; |
| @@ -271,7 +271,8 @@ vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) · 1 unmodified line | |||
| 271 | } | 271 | } |
| 272 | catch (const vm_user_exception &ue) | 272 | catch (const vm_user_exception &ue) |
| 273 | { | 273 | { |
| 274 | _error_message = calloc_memory<char>(max_error_message); | 274 | // [TODO] Include the module and IP to aid in debugging. |
| 275 | _error_message = alloc_memory<char>(max_error_message); | ||
| 275 | const int ec = std::snprintf(_error_message, max_error_message, "%s", ue.what()); | 276 | const int ec = std::snprintf(_error_message, max_error_message, "%s", ue.what()); |
| 276 | assert(ec > 0 && ec < max_error_message); | 277 | assert(ec > 0 && ec < max_error_message); |
| 277 | (void)ec; | 278 | (void)ec; |
| @@ -106,7 +106,7 @@ void *disvm::runtime::alloc_memory(std::size_t amount_in_bytes) · 105 unmodified lines | |||
| 106 | { | 106 | { |
| 107 | safe_alloc s; | 107 | safe_alloc s; |
| 108 | 108 | ||
| 109 | auto memory = std::malloc(amount_in_bytes); | 109 | auto memory = std::calloc(amount_in_bytes, sizeof(byte_t)); |
| 110 | if (memory == nullptr) | 110 | if (memory == nullptr) |
| 111 | throw vm_system_exception{ "Out of memory" }; | 111 | throw vm_system_exception{ "Out of memory" }; |
| 112 | 112 | ||
| @@ -116,20 +116,6 @@ void *disvm::runtime::alloc_memory(std::size_t amount_in_bytes) · 3 unmodified lines | |||
| 116 | return memory; | 116 | return memory; |
| 117 | } | 117 | } |
| 118 | 118 | ||
| 119 | void *disvm::runtime::calloc_memory(std::size_t amount_in_bytes) | ||
| 120 | { | ||
| 121 | safe_alloc s; | ||
| 122 | |||
| 123 | auto memory = std::calloc(amount_in_bytes, sizeof(uint8_t)); | ||
| 124 | if (memory == nullptr) | ||
| 125 | throw vm_system_exception{ "Out of memory" }; | ||
| 126 | |||
| 127 | if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>()) | ||
| 128 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "alloc: zeroed: %#" PRIxPTR " %d", memory, amount_in_bytes); | ||
| 129 | |||
| 130 | return memory; | ||
| 131 | } | ||
| 132 | |||
| 133 | void disvm::runtime::free_memory(void *memory) | 119 | void disvm::runtime::free_memory(void *memory) |
| 134 | { | 120 | { |
| 135 | if (memory == nullptr) | 121 | if (memory == nullptr) |
| @@ -147,27 +133,12 @@ void disvm::runtime::init_memory(const type_descriptor_t &type_desc, void *data) · 11 unmodified lines | |||
| 147 | if (type_desc.size_in_bytes == 0) | 133 | if (type_desc.size_in_bytes == 0) |
| 148 | return; | 134 | return; |
| 149 | 135 | ||
| 150 | debug::assign_debug_memory(data, type_desc.size_in_bytes); | 136 | // [SPEC] The Dis VM spec does not guarantee non-pointer offsets |
| 151 | 137 | // to be initialized to zero, but it was found to be an assumption | |
| 152 | auto memory = reinterpret_cast<word_t *>(data); | 138 | // when getting the limbo compiler run on this VM. Therefore this |
| 153 | for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8) | 139 | // implemetation will make the strong guarantee that memory will |
| 154 | { | 140 | // always be 0 initialized. |
| 155 | const auto words8 = type_desc.pointer_map[i]; | 141 | std::memset(data, 0, type_desc.size_in_bytes); |
| 156 | if (words8 != 0) | ||
| 157 | { | ||
| 158 | const auto flags = std::bitset<sizeof(words8) * 8>{ words8 }; | ||
| 159 | |||
| 160 | // Enumerating the flags in reverse order so memory access is linear. | ||
| 161 | if (flags[7]) memory[0] = runtime_constants::nil; | ||
| 162 | if (flags[6]) memory[1] = runtime_constants::nil; | ||
| 163 | if (flags[5]) memory[2] = runtime_constants::nil; | ||
| 164 | if (flags[4]) memory[3] = runtime_constants::nil; | ||
| 165 | if (flags[3]) memory[4] = runtime_constants::nil; | ||
| 166 | if (flags[2]) memory[5] = runtime_constants::nil; | ||
| 167 | if (flags[1]) memory[6] = runtime_constants::nil; | ||
| 168 | if (flags[0]) memory[7] = runtime_constants::nil; | ||
| 169 | } | ||
| 170 | } | ||
| 171 | } | 142 | } |
| 172 | 143 | ||
| 173 | namespace | 144 | namespace |
| @@ -210,8 +181,6 @@ void disvm::runtime::dec_ref_count_and_free(vm_alloc_t *alloc) · 36 unmodified lines | |||
| 210 | delete alloc; | 181 | delete alloc; |
| 211 | } | 182 | } |
| 212 | 183 | ||
| 213 | const zero_memory_t vm_alloc_t::zero_memory = {}; | ||
| 214 | |||
| 215 | void *vm_alloc_t::operator new(std::size_t sz) | 184 | void *vm_alloc_t::operator new(std::size_t sz) |
| 216 | { | 185 | { |
| 217 | return alloc_memory(sz); | 186 | return alloc_memory(sz); |
| @@ -231,27 +200,10 @@ vm_alloc_t *vm_alloc_t::allocate(std::shared_ptr<const type_descriptor_t> td) · 13 unmodified lines | |||
| 231 | auto mem = alloc_memory(sizeof(vm_alloc_t) + td->size_in_bytes); | 200 | auto mem = alloc_memory(sizeof(vm_alloc_t) + td->size_in_bytes); |
| 232 | auto alloc = ::new(mem)vm_alloc_t{ td }; | 201 | auto alloc = ::new(mem)vm_alloc_t{ td }; |
| 233 | 202 | ||
| 234 | // Initialize pointer types | ||
| 235 | init_memory(*td, alloc->get_allocation()); | ||
| 236 | |||
| 237 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm alloc: %d", td->size_in_bytes); | 203 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm alloc: %d", td->size_in_bytes); |
| 238 | return alloc; | 204 | return alloc; |
| 239 | } | 205 | } |
| 240 | 206 | ||
| 241 | vm_alloc_t *vm_alloc_t::allocate(std::shared_ptr<const type_descriptor_t> td, const zero_memory_t &) | ||
| 242 | { | ||
| 243 | assert(td != nullptr); | ||
| 244 | if (td->size_in_bytes <= 0) | ||
| 245 | throw vm_system_exception{ "Invalid dynamic memory allocation size" }; | ||
| 246 | |||
| 247 | // No need to initialize memory since it is already zeroed out. | ||
| 248 | auto mem = calloc_memory(sizeof(vm_alloc_t) + td->size_in_bytes); | ||
| 249 | auto alloc = ::new(mem)vm_alloc_t{ td }; | ||
| 250 | |||
| 251 | debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "initz: vm alloc: %d", td->size_in_bytes); | ||
| 252 | return alloc; | ||
| 253 | } | ||
| 254 | |||
| 255 | vm_alloc_t *vm_alloc_t::copy(const vm_alloc_t &other) | 207 | vm_alloc_t *vm_alloc_t::copy(const vm_alloc_t &other) |
| 256 | { | 208 | { |
| 257 | auto alloc_copy = vm_alloc_t::allocate(other.alloc_type); | 209 | auto alloc_copy = vm_alloc_t::allocate(other.alloc_type); |