History

Alter debugger API to accept vector value not reference.

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

arr committed 9 years ago

Commit cc8f2b61 parent 41616d412 files · +167 −250Browse files

Showing the changes to src/vm/vm_memory.cpp (1 of 12 files). Show all

Unified · Split

src/vm/vm_memory.cpp+88−87
@@ -20,92 +20,8 @@ · 19 unmodified lines
20using namespace disvm;20using namespace disvm;
21using namespace disvm::runtime;21using namespace disvm::runtime;
22 22
23namespace
24{
25 std::mutex global_lock;
26 std::condition_variable release_lock;
27
28 enum class lock_state_t
29 {
30 none,
31 pending,
32 active
33 };
34
35 std::atomic<uint32_t> allocating_thread_count;
36 std::atomic<lock_state_t> lock_state;
37
38 struct safe_alloc final
39 {
40 safe_alloc()
41 {
42 allocating_thread_count++;
43 if (lock_state != lock_state_t::none)
44 {
45 allocating_thread_count--;
46
47 std::unique_lock<std::mutex> lock{ global_lock };
48 release_lock.wait(lock, [&]{ return lock_state == lock_state_t::none; });
49
50 allocating_thread_count++;
51 }
52 }
53
54 ~safe_alloc()
55 {
56 assert(allocating_thread_count > 0);
57 allocating_thread_count--;
58 }
59 };
60}
61
62global_alloc_lock_t::global_alloc_lock_t(bool owner)
63 : _owner{ owner }
64{ }
65
66global_alloc_lock_t::global_alloc_lock_t(global_alloc_lock_t &&other)
67 : _owner{ other._owner }
68{
69 other._owner = false;
70}
71
72global_alloc_lock_t::~global_alloc_lock_t()
73{
74 if (_owner)
75 {
76 lock_state = lock_state_t::none;
77 release_lock.notify_all();
78 }
79}
80
81// Lock all allocations on the VM heap.
82global_alloc_lock_t disvm::runtime::get_global_alloc_lock()
83{
84 auto expected = lock_state_t::none;
85
86 // Block while trying to get lock
87 while (!lock_state.compare_exchange_strong(expected, lock_state_t::pending))
88 {
89 expected = lock_state_t::none;
90 std::unique_lock<std::mutex> lock{ global_lock };
91 release_lock.wait(lock);
92 }
93
94 while (allocating_thread_count != 0)
95 {
96 // Spin-wait for all allocating threads to stop
97 }
98
99 // Transition to active lock state
100 lock_state = lock_state_t::active;
101
102 return{ true };
103}
104
105void *disvm::runtime::alloc_memory(std::size_t amount_in_bytes)23void *disvm::runtime::alloc_memory(std::size_t amount_in_bytes)
106{24{
107 safe_alloc s;
108
109 auto memory = std::calloc(amount_in_bytes, sizeof(byte_t));25 auto memory = std::calloc(amount_in_bytes, sizeof(byte_t));
110 if (memory == nullptr)26 if (memory == nullptr)
111 throw vm_system_exception{ "Out of memory" };27 throw vm_system_exception{ "Out of memory" };
@@ -143,13 +59,13 @@ void disvm::runtime::init_memory(const type_descriptor_t &type_desc, void *data) · 31 unmodified lines
143 59
144namespace60namespace
145{61{
146 void dec_ref_and_free_pointer_field(pointer_t pointer_field, std::size_t)62 void dec_ref_and_free_pointer_field(pointer_t pointer_field)
147 {63 {
148 assert(pointer_field != nullptr);64 assert(pointer_field != nullptr);
149 dec_ref_count_and_free(vm_alloc_t::from_allocation(pointer_field));65 dec_ref_count_and_free(vm_alloc_t::from_allocation(pointer_field));
150 }66 }
151 67
152 void add_ref_pointer_field(pointer_t pointer_field, std::size_t)68 void add_ref_pointer_field(pointer_t pointer_field)
153 {69 {
154 assert(pointer_field != nullptr);70 assert(pointer_field != nullptr);
155 vm_alloc_t::from_allocation(pointer_field)->add_ref();71 vm_alloc_t::from_allocation(pointer_field)->add_ref();
@@ -184,6 +100,89 @@ void disvm::runtime::dec_ref_count_and_free(vm_alloc_t *alloc) · 28 unmodified lines
184 delete alloc;100 delete alloc;
185}101}
186 102
103void disvm::runtime::enum_pointer_fields(const type_descriptor_t &type_desc, void *data, pointer_field_callback_t callback)
104{
105 assert(data != nullptr && callback != nullptr);
106 if (type_desc.size_in_bytes == 0)
107 return;
108
109 auto offset_accum = std::size_t{ 0 };
110 auto memory = reinterpret_cast<word_t *>(data);
111 for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8, offset_accum += (8 * sizeof(pointer_t)))
112 {
113 const auto words8 = type_desc.pointer_map[i];
114 assert(sizeof(words8) == 1);
115 if (words8 == 0)
116 continue;
117
118 const auto flags = std::bitset<sizeof(words8) * 8>{ words8 };
119
120 // Enumerating the flags in reverse order so memory access is sequential.
121 if (flags[7] && (memory[0] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[0]));
122 if (flags[6] && (memory[1] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[1]));
123 if (flags[5] && (memory[2] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[2]));
124 if (flags[4] && (memory[3] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[3]));
125 if (flags[3] && (memory[4] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[4]));
126 if (flags[2] && (memory[5] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[5]));
127 if (flags[1] && (memory[6] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[6]));
128 if (flags[0] && (memory[7] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[7]));
129 }
130}
131
132void disvm::runtime::enum_pointer_fields_with_offset(const type_descriptor_t &type_desc, void *data, pointer_field_offset_callback_t callback)
133{
134 assert(data != nullptr && callback != nullptr);
135 if (type_desc.size_in_bytes == 0)
136 return;
137
138 auto offset_accum = std::size_t{ 0 };
139 auto memory = reinterpret_cast<word_t *>(data);
140 for (auto i = word_t{ 0 }; i < type_desc.map_in_bytes; ++i, memory += 8, offset_accum += (8 * sizeof(pointer_t)))
141 {
142 const auto words8 = type_desc.pointer_map[i];
143 assert(sizeof(words8) == 1);
144 if (words8 == 0)
145 continue;
146
147 const auto flags = std::bitset<sizeof(words8) * 8>{ words8 };
148
149 // Enumerating the flags in reverse order so memory access is sequential.
150 if (flags[7] && (memory[0] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[0]), offset_accum);
151 if (flags[6] && (memory[1] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[1]), offset_accum + (1 * sizeof(pointer_t)));
152 if (flags[5] && (memory[2] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[2]), offset_accum + (2 * sizeof(pointer_t)));
153 if (flags[4] && (memory[3] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[3]), offset_accum + (3 * sizeof(pointer_t)));
154 if (flags[3] && (memory[4] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[4]), offset_accum + (4 * sizeof(pointer_t)));
155 if (flags[2] && (memory[5] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[5]), offset_accum + (5 * sizeof(pointer_t)));
156 if (flags[1] && (memory[6] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[6]), offset_accum + (6 * sizeof(pointer_t)));
157 if (flags[0] && (memory[7] != runtime_constants::nil)) callback(reinterpret_cast<pointer_t>(memory[7]), offset_accum + (7 * sizeof(pointer_t)));
158 }
159}
160
161bool disvm::runtime::is_offset_pointer(const type_descriptor_t &type_desc, std::size_t offset)
162{
163 if (type_desc.size_in_bytes == 0)
164 return false;
165
166 assert(offset < static_cast<std::size_t>(std::numeric_limits<word_t>::max()));
167 const auto byte_offset = static_cast<word_t>(offset / 8);
168
169 if (byte_offset < type_desc.map_in_bytes)
170 {
171 const auto words8 = type_desc.pointer_map[byte_offset];
172 assert(sizeof(words8) == 1);
173 if (words8 != 0)
174 {
175 const auto flags = std::bitset<sizeof(words8) * 8>{ words8 };
176
177 // Highest order bit is the first field
178 const auto bit_offset = 7 - (offset % 8);
179 return flags[bit_offset];
180 }
181 }
182
183 return false;
184}
185
187void *vm_alloc_t::operator new(std::size_t sz)186void *vm_alloc_t::operator new(std::size_t sz)
188{187{
189 return alloc_memory(sz);188 return alloc_memory(sz);
@@ -203,7 +202,9 @@ vm_alloc_t *vm_alloc_t::allocate(std::shared_ptr<const type_descriptor_t> td) · 13 unmodified lines
203 auto mem = alloc_memory(sizeof(vm_alloc_t) + td->size_in_bytes);202 auto mem = alloc_memory(sizeof(vm_alloc_t) + td->size_in_bytes);
204 auto alloc = ::new(mem)vm_alloc_t{ td };203 auto alloc = ::new(mem)vm_alloc_t{ td };
205 204
206 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm alloc: %d", td->size_in_bytes);205 if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>())
206 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm alloc: %d", td->size_in_bytes);
207
207 return alloc;208 return alloc;
208}209}
209 210