History

Initial public commit for Dis VM

arr committed 10 years ago

Commit f95902eNo parent (first commit)40 files · +9358 −0Browse files

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

Unified · Split

src/vm/stack.cpp+314−0
@@ -0,0 +1,314 @@
1//
2// Dis VM
3// File: stack.cpp
4// Author: arr
5//
6
7#include <cinttypes>
8#include <runtime.h>
9#include <builtin_module.h>
10#include <vm_memory.h>
11#include <debug.h>
12#include <exceptions.h>
13
14using namespace disvm;
15using namespace disvm::runtime;
16
17namespace
18{
19 struct vm_stack_page
20 {
21 vm_stack_page *prev_page;
22 uintptr_t page_limit_addr;
23 vm_frame_t *page_top_frame; // Do not free. This is used as a tracking pointer.
24
25 void *stack_data() const
26 {
27 // The beginning of the stack data is at the end of the struct.
28 return reinterpret_cast<uint8_t *>(const_cast<vm_stack_page *>(this)) + sizeof(vm_stack_page);
29 }
30
31 bool contains_frame(const vm_frame_t *frame) const
32 {
33 return reinterpret_cast<uintptr_t>(stack_data()) <= reinterpret_cast<uintptr_t>(frame) && reinterpret_cast<uintptr_t>(frame) < page_limit_addr;
34 }
35 };
36
37 struct vm_stack_layout final : public vm_alloc_t
38 {
39 static std::shared_ptr<const type_descriptor_t> type_desc()
40 {
41 static auto td = type_descriptor_t::create(0);
42 return td;
43 }
44
45 vm_stack_layout(std::size_t stack_page_size)
46 : vm_alloc_t(type_desc())
47 , stack_page_size{ stack_page_size }
48 , top_frame{ nullptr }
49 , top_page{ nullptr }
50 {
51 new_page();
52 }
53
54 ~vm_stack_layout()
55 {
56 assert(top_page != nullptr);
57 free_memory(top_page);
58 }
59
60 const std::size_t stack_page_size;
61
62 vm_stack_page *top_page;
63 vm_frame_t *top_frame; // Do not free. This is used as a tracking pointer.
64
65 // Create a new 'top page' and return stack data address
66 void *new_page()
67 {
68 auto current_stack_page = top_page;
69 top_page = alloc_memory<vm_stack_page>(sizeof(vm_stack_page) + stack_page_size);
70
71 // Initialize the stack allocation.
72 top_page->prev_page = current_stack_page;
73
74 // The top frame member is only for tracking and so is initialized to null.
75 top_page->page_top_frame = nullptr;
76 top_page->page_limit_addr = reinterpret_cast<uintptr_t>(top_page)+stack_page_size;
77
78 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "alloc: vm stack alloc: %#" PRIxPTR " %#" PRIxPTR "\n", top_page, top_page->page_limit_addr);
79
80 return top_page->stack_data();
81 }
82
83 // Drop the current page and move to the previous one
84 void drop_page()
85 {
86 auto current_stack_page = top_page->prev_page;
87 assert(current_stack_page != nullptr);
88
89 free_memory(top_page);
90 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "free: vm stack alloc\n");
91
92 // Set the top page
93 top_page = current_stack_page;
94 }
95 };
96}
97
98vm_frame_t::vm_frame_t(std::shared_ptr<const type_descriptor_t> &td)
99 : frame_type{ std::move(td) }
100{
101 assert(frame_type != nullptr);
102
103 // Initialize the new frame
104 init_memory(*frame_type, base());
105
106 prev_pc() = runtime_constants::invalid_program_counter;
107 prev_frame() = nullptr;
108 prev_module_ref() = nullptr;
109
110 if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>())
111 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm frame: %#" PRIxPTR " %d\n", this, frame_type->size_in_bytes);
112}
113
114vm_frame_t::~vm_frame_t()
115{
116 dec_ref_count_and_free(prev_module_ref());
117
118 destroy_memory(*frame_type, base());
119
120 if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>())
121 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: vm frame: %#" PRIxPTR "\n", this);
122}
123
124void vm_frame_t::copy_frame_contents(const vm_frame_t &other)
125{
126 auto this_frame_base = reinterpret_cast<uint8_t *>(base());
127 auto this_frame_contents = this_frame_base + sizeof(vm_frame_base_alloc_t);
128
129 auto other_frame_base = reinterpret_cast<uint8_t *>(other.base());
130 auto other_frame_contents = other_frame_base + sizeof(vm_frame_base_alloc_t);
131
132 assert(frame_type->size_in_bytes > sizeof(vm_frame_base_alloc_t));
133 std::memcpy(this_frame_contents, other_frame_contents, frame_type->size_in_bytes - sizeof(vm_frame_base_alloc_t));
134
135 inc_ref_count_in_memory(*frame_type, this_frame_base);
136}
137
138pointer_t vm_frame_t::base() const
139{
140 // The beginning of the frame data is at the end of the frame struct.
141 return reinterpret_cast<pointer_t>(reinterpret_cast<uint8_t *>(const_cast<vm_frame_t *>(this)) + sizeof(vm_frame_t));
142}
143
144vm_pc_t &vm_frame_t::prev_pc() const
145{
146 return base<vm_frame_base_alloc_t>().p_pc;
147}
148
149vm_frame_t *&vm_frame_t::prev_frame() const
150{
151 return base<vm_frame_base_alloc_t>().p_fp;
152}
153
154vm_module_ref_t *&vm_frame_t::prev_module_ref() const
155{
156 return base<vm_frame_base_alloc_t>().p_mr;
157}
158
159pointer_t vm_frame_t::fixed_point_register_1() const
160{
161 const auto offset = runtime_constants::fixed_point_register_1_frame_offset;
162 assert(offset < frame_type->size_in_bytes);
163
164 auto p = reinterpret_cast<uint8_t *>(base()) + offset;
165 return reinterpret_cast<pointer_t>(p);
166}
167
168pointer_t vm_frame_t::fixed_point_register_2() const
169{
170 const auto offset = runtime_constants::fixed_point_register_2_frame_offset;
171 assert(offset < frame_type->size_in_bytes);
172
173 auto p = reinterpret_cast<uint8_t *>(base()) + offset;
174 return reinterpret_cast<pointer_t>(p);
175}
176
177vm_stack_t::vm_stack_t(std::size_t stack_extent)
178 : _mem{ std::make_unique<vm_stack_layout>(stack_extent) }
179{
180 assert(stack_extent != 0);
181 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "init: vm stack\n");
182}
183
184vm_stack_t::~vm_stack_t()
185{
186 while (nullptr != pop_frame())
187 {
188 // Pop all frames off the stack
189 }
190
191 // Free stack memory
192 _mem->release();
193 _mem.reset();
194
195 debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: vm stack\n");
196}
197
198vm_frame_t *vm_stack_t::alloc_frame(std::shared_ptr<const type_descriptor_t> frame_type)
199{
200 assert(frame_type != nullptr);
201 assert(sizeof(vm_frame_base_alloc_t) < frame_type->size_in_bytes && "Requested frame size less than VM frame base size");
202
203 auto layout = dynamic_cast<vm_stack_layout *>(_mem.get());
204
205 auto page_top_frame = layout->top_page->page_top_frame;
206 const auto new_frame_size = sizeof(vm_frame_t) + frame_type->size_in_bytes;
207 if (layout->stack_page_size < new_frame_size)
208 throw vm_system_exception{ "Requested stack frame larger than stack page" };
209
210 vm_frame_t *new_frame;
211 if (page_top_frame == nullptr)
212 {
213 new_frame = ::new(layout->top_page->stack_data())vm_frame_t{ frame_type };
214 }
215 else
216 {
217 auto current_page_top_frame_addr = reinterpret_cast<std::size_t>(page_top_frame);
218 auto new_frame_addr_maybe = current_page_top_frame_addr + (sizeof(vm_frame_t) + page_top_frame->frame_type->size_in_bytes);
219
220 // If the new frame is not going to fit on the current page, create a new page.
221 if ((new_frame_addr_maybe + new_frame_size) >= layout->top_page->page_limit_addr)
222 new_frame_addr_maybe = reinterpret_cast<uintptr_t>(layout->new_page());
223
224 new_frame = ::new(reinterpret_cast<void *>(new_frame_addr_maybe))vm_frame_t{ frame_type };
225 }
226
227 // Record the top frame on the page
228 layout->top_page->page_top_frame = new_frame;
229
230 return new_frame;
231}
232
233vm_frame_t *vm_stack_t::push_frame()
234{
235 auto layout = dynamic_cast<vm_stack_layout *>(_mem.get());
236
237 auto current_top_frame = layout->top_frame;
238 auto new_top_frame = layout->top_page->page_top_frame;
239 new_top_frame->prev_frame() = current_top_frame;
240
241 layout->top_frame = new_top_frame;
242
243 if (debug::is_component_tracing_enabled<debug::component_trace_t::stack>())
244 debug::log_msg(debug::component_trace_t::stack, debug::log_level_t::debug, "update: push vm frame: %#" PRIxPTR "\n", new_top_frame);
245
246 return new_top_frame;
247}
248
249vm_frame_t *vm_stack_t::pop_frame()
250{
251 auto layout = dynamic_cast<vm_stack_layout *>(_mem.get());
252
253 auto current_frame = layout->top_frame;
254 if (current_frame == nullptr)
255 return nullptr;
256
257 {
258 // Dispose of the current top frame and update the current frame.
259 auto previous_frame = current_frame;
260 current_frame = previous_frame->prev_frame();
261
262 // Call destructor on previous frame since we allocated
263 // the frame on the stack page.
264 previous_frame->~vm_frame_t();
265 }
266
267 layout->top_frame = current_frame;
268 layout->top_page->page_top_frame = current_frame;
269
270 // Check if the stack page is no longer needed.
271 if (current_frame != nullptr)
272 {
273 // Clean up the stack page allocation if the current frame is on a previous page
274 if (!layout->top_page->contains_frame(current_frame))
275 {
276 layout->drop_page();
277
278 // Ensure the current frame is in the new page.
279 assert(layout->top_page->contains_frame(current_frame));
280 }
281 }
282
283 if (debug::is_component_tracing_enabled<debug::component_trace_t::stack>())
284 debug::log_msg(debug::component_trace_t::stack, debug::log_level_t::debug, "update: pop vm frame: %#" PRIxPTR "\n", current_frame);
285
286 return current_frame;
287}
288
289vm_frame_t *vm_stack_t::peek_frame() const
290{
291 auto layout = dynamic_cast<vm_stack_layout *>(_mem.get());
292 return layout->top_frame;
293}
294
295void vm_stack_t::walk_stack(vm_stack_walk_callback_t callback) const
296{
297 if (callback == nullptr)
298 return;
299
300 auto layout = dynamic_cast<vm_stack_layout *>(_mem.get());
301 auto current_page = layout->top_page;
302 auto current_frame = layout->top_frame;
303
304 while (current_page != nullptr && current_frame != nullptr)
305 {
306 if (!callback(current_frame, current_page))
307 break;
308
309 current_frame = current_frame->prev_frame();
310
311 if (!current_page->contains_frame(current_frame))
312 current_page = current_page->prev_page;
313 }
314}