History

DisVM will now only use 0 initialized memory. - The alloc_memory function will only return 0 initialize memory - calloc_memory was removed

Floating point division is now relying on underlying FPU and IEEE for behavior with respect to divide by 0

arr committed 9 years ago

Commit b92328e1 parent bd1f49311 files · +48 −112Browse files

Unified · Split

11 files
  1. src/exec/main.cpp +1 −1
  2. src/include/debug.h +1 −1
  3. src/include/runtime.h +0 −8
  4. src/include/vm_memory.h +3 −10
  5. src/vm/array.cpp +0 −13
  6. src/vm/execution_table.cpp +23 −9
  7. src/vm/module_reader.cpp +1 −4
  8. src/vm/stack.cpp +1 −1
  9. src/vm/string.cpp +7 −7
  10. src/vm/thread.cpp +4 −3
  11. src/vm/vm_memory.cpp +7 −55
src/exec/main.cpp+1−1
@@ -91,7 +91,7 @@ namespace · 90 unmodified lines
9191
9292 // Initialize the module base register
9393 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));
9595 auto mp_base = e.original_mp->get_allocation<pointer_t>();
9696
9797 // Take a reference on the command module path and store it on the entry frame
src/include/debug.h+1−1
@@ -91,7 +91,7 @@ namespace disvm · 90 unmodified lines
9191 {
9292 #ifndef NDEBUG
9393 assert(t != nullptr);
94− *t = reinterpret_cast<T *>(0xbada110c);
94+ assign_debug_memory(t, sizeof(t));
9595 #endif
9696 }
9797 }
src/include/runtime.h+0−8
@@ -89,21 +89,14 @@ namespace disvm · 88 unmodified lines
8989 static std::shared_ptr<const type_descriptor_t> type();
9090 };
9191
92− // Type used to indicate allocated memory should be zeroed out.
93− struct zero_memory_t
94− { };
95−
9692 // VM memory allocation
9793 class vm_alloc_t
9894 {
9995 public: // static
100− static const zero_memory_t zero_memory;
101−
10296 static void *operator new(std::size_t sz);
10397 static void operator delete(void *ptr);
10498
10599 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 &);
107100 static vm_alloc_t *copy(const vm_alloc_t &other);
108101 static vm_alloc_t *from_allocation(pointer_t allocation);
109102
@@ -205,7 +198,6 @@ namespace disvm · 95 unmodified lines
205198
206199 public:
207200 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&);
209201 vm_array_t(vm_array_t *original, word_t begin_index, word_t length);
210202 vm_array_t(const vm_string_t *s);
211203 ~vm_array_t();
src/include/vm_memory.h+3−10
@@ -40,7 +40,9 @@ namespace disvm · 39 unmodified lines
4040 // While this lock is taken all further allocations will block.
4141 global_alloc_lock_t get_global_alloc_lock();
4242
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.
4446 void *alloc_memory(std::size_t amount_in_bytes);
4547
4648 template<typename T>
@@ -49,15 +51,6 @@ namespace disvm · 2 unmodified lines
4951 return reinterpret_cast<T *>(alloc_memory(amount_in_bytes));
5052 }
5153
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−
6154 // Free memory on the VM heap
6255 void free_memory(void *memory);
6356
src/vm/array.cpp+0−13
@@ -43,19 +43,6 @@ vm_array_t::vm_array_t(std::shared_ptr<const type_descriptor_t> td, word_t lengt · 42 unmodified lines
4343 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "end: init: elements: %d", array_len);
4444 }
4545
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−
5946 vm_array_t::vm_array_t(vm_array_t *original, word_t begin_index, word_t length)
6047 : vm_alloc_t(vm_array_t::type_desc())
6148 , _arr{ nullptr }
src/vm/execution_table.cpp+23−9
@@ -294,6 +294,16 @@ namespace · 293 unmodified lines
294294 vt_ref<PrimitiveType>(r.dest) = vt_ref<PrimitiveType>(r.mid) / denom;
295295 }
296296
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+
297307 EXEC_DECL(divb) { _div<byte_t>(r); }
298308 EXEC_DECL(divw) { _div<word_t>(r); }
299309 EXEC_DECL(divf) { _div<real_t>(r); }
@@ -1318,7 +1328,7 @@ namespace · 1018 unmodified lines
13181328
13191329 namespace
13201330 {
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)
13221332 {
13231333 const auto type_id = vt_ref<word_t>(r.src);
13241334 const auto &types = r.module_ref->type_section;
@@ -1327,7 +1337,7 @@ namespace · 2 unmodified lines
13271337 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
13281338
13291339 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);
13311341
13321342 if (type_desc->map_in_bytes > 0)
13331343 vm.get_garbage_collector().track_allocation(new_alloc);
@@ -1338,12 +1348,14 @@ namespace · 4 unmodified lines
13381348
13391349 EXEC_DECL(new_)
13401350 {
1341− new_instance(r, vm, false /* zero memory */);
1351+ new_instance(r, vm);
13421352 }
13431353
13441354 EXEC_DECL(newz)
13451355 {
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);
13471359 }
13481360
13491361 EXEC_DECL(mnewz)
@@ -1359,7 +1371,7 @@ namespace · 9 unmodified lines
13591371 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
13601372
13611373 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);
13631375
13641376 if (type_desc->map_in_bytes > 0)
13651377 vm.get_garbage_collector().track_allocation(new_alloc);
@@ -1369,7 +1381,7 @@ namespace · 3 unmodified lines
13691381
13701382 namespace
13711383 {
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)
13731385 {
13741386 assert(r.module_ref != nullptr);
13751387 const auto &types = r.module_ref->type_section;
@@ -1381,7 +1393,7 @@ namespace · 5 unmodified lines
13811393 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
13821394
13831395 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);
13851397
13861398 if (type_desc->map_in_bytes > 0)
13871399 vm.get_garbage_collector().track_allocation(new_array);
@@ -1392,12 +1404,14 @@ namespace · 4 unmodified lines
13921404
13931405 EXEC_DECL(newa)
13941406 {
1395− new_array(r, vm, false /* zero memory */);
1407+ new_array(r, vm);
13961408 }
13971409
13981410 EXEC_DECL(newaz)
13991411 {
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);
14011415 }
14021416
14031417 EXEC_DECL(tcmp)
src/vm/module_reader.cpp+1−4
@@ -362,10 +362,7 @@ namespace · 361 unmodified lines
362362 {
363363 auto vm_module_type = modobj.type_section[module_constants::vm_module_type_desc_number];
364364 if (vm_module_type->size_in_bytes != modobj.header.data_size) throw module_reader_exception{ "Invalid type desc for MP" };
365−
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));
365+ modobj.original_mp.reset(vm_alloc_t::allocate(vm_module_type));
369366 }
370367
371368 auto array_stack = std::array<byte_t *, module_constants::array_address_stack_size>{};
src/vm/stack.cpp+1−1
@@ -66,7 +66,7 @@ namespace · 65 unmodified lines
6666 void *new_page()
6767 {
6868 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);
7070
7171 // Initialize the stack allocation.
7272 top_page->prev_page = current_stack_page;
src/vm/string.cpp+7−7
@@ -217,7 +217,7 @@ vm_string_t::vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str · 216 unmodified lines
217217 {
218218 // If we are going to allocate, make sure it is larger than needed.
219219 _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);
221221 _is_alloc = true;
222222
223223 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
248248 , _length_max{ compute_max_length(s1._length + s2._length) }
249249 , _mem{}
250250 {
251− _mem.alloc = calloc_memory<uint8_t>(_length_max * _character_size);
251+ _mem.alloc = alloc_memory<uint8_t>(_length_max * _character_size);
252252
253253 const auto s1_data = (s1._is_alloc) ? s1._mem.alloc : s1._mem.local;
254254 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
290290 if (_is_alloc)
291291 {
292292 src = other._mem.alloc;
293− _mem.alloc = calloc_memory<uint8_t>(_length_max);
293+ _mem.alloc = alloc_memory<uint8_t>(_length_max);
294294 dest = _mem.alloc;
295295 }
296296
@@ -358,9 +358,9 @@ vm_string_t &vm_string_t::append(const vm_string_t &other) · 61 unmodified lines
358358
359359 // Check if this string and the supplied string have the same character size.
360360 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));
362362 else
363− new_source = calloc_memory<uint8_t>(new_length_max);
363+ new_source = alloc_memory<uint8_t>(new_length_max);
364364
365365 // Combine the two string sources
366366 combine(
@@ -449,7 +449,7 @@ void vm_string_t::set_rune(word_t index, rune_t value) · 82 unmodified lines
449449 assert(_character_size == sizeof(rune_t));
450450
451451 _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);
453453
454454 // Copy source into the new alloc.
455455 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
467467 else if (static_cast<word_t>(_length_max) <= index) // Check if string can accomodate the new character.
468468 {
469469 _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);
471471 std::memcpy(new_alloc, source, _length * _character_size);
472472
473473 // Free memory, if allocated
src/vm/thread.cpp+4−3
@@ -255,7 +255,7 @@ vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) · 254 unmodified lines
255255 }
256256 catch (const unhandled_user_exception &uue)
257257 {
258− _error_message = calloc_memory<char>(max_error_message);
258+ _error_message = alloc_memory<char>(max_error_message);
259259 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);
260260 assert(ec > 0 && ec < max_error_message);
261261 (void)ec;
@@ -263,7 +263,7 @@ vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) · 1 unmodified line
263263 }
264264 catch (const index_out_of_range_memory &ioor)
265265 {
266− _error_message = calloc_memory<char>(max_error_message);
266+ _error_message = alloc_memory<char>(max_error_message);
267267 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);
268268 assert(ec > 0 && ec < max_error_message);
269269 (void)ec;
@@ -271,7 +271,8 @@ vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) · 1 unmodified line
271271 }
272272 catch (const vm_user_exception &ue)
273273 {
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);
275276 const int ec = std::snprintf(_error_message, max_error_message, "%s", ue.what());
276277 assert(ec > 0 && ec < max_error_message);
277278 (void)ec;
src/vm/vm_memory.cpp+7−55
@@ -106,7 +106,7 @@ void *disvm::runtime::alloc_memory(std::size_t amount_in_bytes) · 105 unmodified lines
106106 {
107107 safe_alloc s;
108108
109− auto memory = std::malloc(amount_in_bytes);
109+ auto memory = std::calloc(amount_in_bytes, sizeof(byte_t));
110110 if (memory == nullptr)
111111 throw vm_system_exception{ "Out of memory" };
112112
@@ -116,20 +116,6 @@ void *disvm::runtime::alloc_memory(std::size_t amount_in_bytes) · 3 unmodified lines
116116 return memory;
117117 }
118118
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−
133119 void disvm::runtime::free_memory(void *memory)
134120 {
135121 if (memory == nullptr)
@@ -147,27 +133,12 @@ void disvm::runtime::init_memory(const type_descriptor_t &type_desc, void *data) · 11 unmodified lines
147133 if (type_desc.size_in_bytes == 0)
148134 return;
149135
150− debug::assign_debug_memory(data, type_desc.size_in_bytes);
151−
152− auto memory = reinterpret_cast<word_t *>(data);
153− for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8)
154− {
155− const auto words8 = type_desc.pointer_map[i];
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− }
136+ // [SPEC] The Dis VM spec does not guarantee non-pointer offsets
137+ // to be initialized to zero, but it was found to be an assumption
138+ // when getting the limbo compiler run on this VM. Therefore this
139+ // implemetation will make the strong guarantee that memory will
140+ // always be 0 initialized.
141+ std::memset(data, 0, type_desc.size_in_bytes);
171142 }
172143
173144 namespace
@@ -210,8 +181,6 @@ void disvm::runtime::dec_ref_count_and_free(vm_alloc_t *alloc) · 36 unmodified lines
210181 delete alloc;
211182 }
212183
213−const zero_memory_t vm_alloc_t::zero_memory = {};
214−
215184 void *vm_alloc_t::operator new(std::size_t sz)
216185 {
217186 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
231200 auto mem = alloc_memory(sizeof(vm_alloc_t) + td->size_in_bytes);
232201 auto alloc = ::new(mem)vm_alloc_t{ td };
233202
234− // Initialize pointer types
235− init_memory(*td, alloc->get_allocation());
236−
237203 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm alloc: %d", td->size_in_bytes);
238204 return alloc;
239205 }
240206
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−
255207 vm_alloc_t *vm_alloc_t::copy(const vm_alloc_t &other)
256208 {
257209 auto alloc_copy = vm_alloc_t::allocate(other.alloc_type);