Remove global memory lock mechanism - Wasn't being used but showed up on profile report :( Move enum_pointer_fields and is_offset_pointer impls into cpp file Create enum_pointer_fields_with_offset function since the VM never needs the offset values and the generated code was about 5% bigger. Now the VM can use the callback without the offset. Removed pointless init_memory() usage since alloc_memory() now is required to return zeroed out memory. Fixed bugs in GC Added support - Ranged absolute disassembly in debugger - New duration log component
Showing the changes to src/include (3 of 12 files). Show all
Unified · Split
| @@ -31,9 +31,10 @@ namespace disvm · 30 unmodified lines | ||
| 31 | 31 | // Potentially logged from a VM extension. |
| 32 | 32 | scheduler, |
| 33 | 33 | garbage_collector, |
| 34 | + duration, | |
| 34 | 35 | |
| 35 | 36 | // Tracking purposes |
| 36 | − last = garbage_collector | |
| 37 | + last = duration | |
| 37 | 38 | }; |
| 38 | 39 | |
| 39 | 40 | // Trace component enable state. |
| @@ -104,7 +104,7 @@ namespace disvm · 103 unmodified lines | ||
| 104 | 104 | static T *from_allocation(pointer_t allocation) |
| 105 | 105 | { |
| 106 | 106 | auto alloc_inst = vm_alloc_t::from_allocation(allocation); |
| 107 | − assert(alloc_inst == nullptr || alloc_inst->alloc_type == intrinsic_type_desc::type<T>()); | |
| 107 | + assert(alloc_inst == nullptr || alloc_inst->alloc_type->is_equal(intrinsic_type_desc::type<T>().get())); | |
| 108 | 108 | return static_cast<T *>(alloc_inst); |
| 109 | 109 | } |
| 110 | 110 | |
| @@ -875,7 +875,7 @@ namespace disvm · 764 unmodified lines | ||
| 875 | 875 | // Called by the scheduler to inform the collector it should attempt to free resources. |
| 876 | 876 | // All VM threads are guaranteed to be idle when called. |
| 877 | 877 | // Returns true if the collection algorithm was run, otherwise false. |
| 878 | − virtual bool collect(std::vector<std::shared_ptr<const vm_thread_t>> &threads) = 0; | |
| 878 | + virtual bool collect(std::vector<std::shared_ptr<const vm_thread_t>> threads) = 0; | |
| 879 | 879 | }; |
| 880 | 880 | |
| 881 | 881 | // Create a garbage collector that does nothing. |
| @@ -20,26 +20,6 @@ namespace disvm · 19 unmodified lines | ||
| 20 | 20 | { |
| 21 | 21 | namespace runtime |
| 22 | 22 | { |
| 23 | − class global_alloc_lock_t final | |
| 24 | − { | |
| 25 | − public: | |
| 26 | − global_alloc_lock_t(const global_alloc_lock_t &) = delete; | |
| 27 | − global_alloc_lock_t& operator=(const global_alloc_lock_t &) = delete; | |
| 28 | − | |
| 29 | − global_alloc_lock_t(global_alloc_lock_t &&); | |
| 30 | − | |
| 31 | − ~global_alloc_lock_t(); | |
| 32 | − | |
| 33 | − private: | |
| 34 | − bool _owner; | |
| 35 | − global_alloc_lock_t(bool owner); | |
| 36 | − friend global_alloc_lock_t get_global_alloc_lock(); | |
| 37 | − }; | |
| 38 | − | |
| 39 | − // Take the global allocation lock for the VM heap. | |
| 40 | − // While this lock is taken all further allocations will block. | |
| 41 | − global_alloc_lock_t get_global_alloc_lock(); | |
| 42 | − | |
| 43 | 23 | // Allocate memory from the VM heap that is initialize to 0. |
| 44 | 24 | // [SPEC] All memory in this implementation of the Dis VM spec |
| 45 | 25 | // will be 0 initialized. |
| @@ -69,63 +49,20 @@ namespace disvm · 23 unmodified lines | ||
| 69 | 49 | // Decrement the ref count and if 0 free the allocation |
| 70 | 50 | void dec_ref_count_and_free(vm_alloc_t *alloc); |
| 71 | 51 | |
| 52 | + // Callback for a pointer field supplying the pointer | |
| 53 | + using pointer_field_callback_t = std::function<void(pointer_t)>; | |
| 54 | + | |
| 55 | + // The callback will be called on all non-null pointer fields. | |
| 56 | + void enum_pointer_fields(const type_descriptor_t &type_desc, void *data, pointer_field_callback_t callback); | |
| 57 | + | |
| 72 | 58 | // Callback for a pointer field supplying the pointer and byte offset |
| 73 | − using pointer_field_callback_t = std::function<void(pointer_t, std::size_t)>; | |
| 59 | + using pointer_field_offset_callback_t = std::function<void(pointer_t, std::size_t)>; | |
| 74 | 60 | |
| 75 | 61 | // The callback will be called on all non-null pointer fields. |
| 76 | − inline void enum_pointer_fields(const type_descriptor_t &type_desc, void *data, pointer_field_callback_t callback) | |
| 77 | − { | |
| 78 | − assert(data != nullptr && callback != nullptr); | |
| 79 | − if (type_desc.size_in_bytes == 0) | |
| 80 | − return; | |
| 81 | − | |
| 82 | − auto offset_accum = std::size_t{ 0 }; | |
| 83 | − auto memory = reinterpret_cast<word_t *>(data); | |
| 84 | − for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8, offset_accum += (8 * sizeof(pointer_t))) | |
| 85 | − { | |
| 86 | − const auto words8 = type_desc.pointer_map[i]; | |
| 87 | − assert(sizeof(words8) == 1); | |
| 88 | − if (words8 == 0) | |
| 89 | − continue; | |
| 90 | − | |
| 91 | − const auto flags = std::bitset<sizeof(words8) * 8>{ words8 }; | |
| 92 | − | |
| 93 | − // Enumerating the flags in reverse order so memory access is sequential. | |
| 94 | − if (flags[7] && (memory[0] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[0]), offset_accum); | |
| 95 | − if (flags[6] && (memory[1] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[1]), offset_accum + (1 * sizeof(pointer_t))); | |
| 96 | − if (flags[5] && (memory[2] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[2]), offset_accum + (2 * sizeof(pointer_t))); | |
| 97 | − if (flags[4] && (memory[3] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[3]), offset_accum + (3 * sizeof(pointer_t))); | |
| 98 | − if (flags[3] && (memory[4] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[4]), offset_accum + (4 * sizeof(pointer_t))); | |
| 99 | − if (flags[2] && (memory[5] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[5]), offset_accum + (5 * sizeof(pointer_t))); | |
| 100 | − if (flags[1] && (memory[6] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[6]), offset_accum + (6 * sizeof(pointer_t))); | |
| 101 | − if (flags[0] && (memory[7] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[7]), offset_accum + (7 * sizeof(pointer_t))); | |
| 102 | − } | |
| 103 | − } | |
| 62 | + void enum_pointer_fields_with_offset(const type_descriptor_t &type_desc, void *data, pointer_field_offset_callback_t callback); | |
| 104 | 63 | |
| 105 | 64 | // Check if a specific pointer-sized offset in a type represents a pointer |
| 106 | − inline bool is_offset_pointer(const type_descriptor_t &type_desc, std::size_t offset) | |
| 107 | − { | |
| 108 | − if (type_desc.size_in_bytes == 0) | |
| 109 | − return false; | |
| 110 | − | |
| 111 | − assert(offset < static_cast<std::size_t>(std::numeric_limits<word_t>::max())); | |
| 112 | − const auto byte_offset = static_cast<word_t>(offset / 8); | |
| 113 | − | |
| 114 | − // Highest order bit is the first field | |
| 115 | − const auto bit_offset = 7 - (offset % 8); | |
| 116 | − if (byte_offset < type_desc.map_in_bytes) | |
| 117 | − { | |
| 118 | − const auto words8 = type_desc.pointer_map[byte_offset]; | |
| 119 | − assert(sizeof(words8) == 1); | |
| 120 | − if (words8 != 0) | |
| 121 | − { | |
| 122 | − const auto flags = std::bitset<sizeof(words8) * 8>{ words8 }; | |
| 123 | − return flags[bit_offset]; | |
| 124 | − } | |
| 125 | − } | |
| 126 | − | |
| 127 | − return false; | |
| 128 | − } | |
| 65 | + bool is_offset_pointer(const type_descriptor_t &type_desc, std::size_t offset); | |
| 129 | 66 | } |
| 130 | 67 | } |
| 131 | 68 | |