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+
14+using namespace disvm;
15+using namespace disvm::runtime;
16+
17+namespace
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+
98+vm_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+
114+vm_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+
124+void 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+
138+pointer_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+
144+vm_pc_t &vm_frame_t::prev_pc() const
145+{
146+ return base<vm_frame_base_alloc_t>().p_pc;
147+}
148+
149+vm_frame_t *&vm_frame_t::prev_frame() const
150+{
151+ return base<vm_frame_base_alloc_t>().p_fp;
152+}
153+
154+vm_module_ref_t *&vm_frame_t::prev_module_ref() const
155+{
156+ return base<vm_frame_base_alloc_t>().p_mr;
157+}
158+
159+pointer_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+
168+pointer_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+
177+vm_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+
184+vm_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+
198+vm_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+
233+vm_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+
249+vm_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+
289+vm_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+
295+void 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+}