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/thread.cpp (1 of 40 files). Show all

Unified · Split

src/vm/thread.cpp+298−0
@@ -0,0 +1,298 @@
1+//
2+// Dis VM
3+// File: thread.cpp
4+// Author: arr
5+//
6+
7+#include <cinttypes>
8+#include <cassert>
9+#include <memory>
10+#include <atomic>
11+#include <disvm.h>
12+#include <exceptions.h>
13+#include <vm_memory.h>
14+#include <debug.h>
15+#include "execution_table.h"
16+
17+using namespace disvm;
18+using namespace disvm::runtime;
19+
20+namespace
21+{
22+ // Update VM registers based on the instruction.
23+ void decode_address(const vm_exec_op_t &inst, vm_registers_t &reg)
24+ {
25+ switch (inst.source.mode)
26+ {
27+ case address_mode_t::offset_indirect_fp:
28+ reg.src = reinterpret_cast<pointer_t>(reinterpret_cast<uint8_t *>(reg.stack.peek_frame()->base()) + inst.source.register1);
29+ break;
30+ case address_mode_t::offset_indirect_mp:
31+ reg.src = reinterpret_cast<pointer_t>(reinterpret_cast<uint8_t *>(reg.mp_base->allocation()) + inst.source.register1);
32+ break;
33+ case address_mode_t::offset_double_indirect_fp:
34+ {
35+ const auto frame_offset = *reinterpret_cast<std::size_t *>(reinterpret_cast<uint8_t *>(reg.stack.peek_frame()->base()) + inst.source.register1);
36+ reg.src = reinterpret_cast<pointer_t>(frame_offset + inst.source.register2);
37+ break;
38+ }
39+ case address_mode_t::offset_double_indirect_mp:
40+ {
41+ const auto mp_offset = *reinterpret_cast<std::size_t *>(reinterpret_cast<uint8_t *>(reg.mp_base->allocation()) + inst.source.register1);
42+ reg.src = reinterpret_cast<pointer_t>(mp_offset + inst.source.register2);
43+ break;
44+ }
45+ case address_mode_t::immediate:
46+ reg.src = reinterpret_cast<pointer_t>(const_cast<word_t *>(&inst.source.register1));
47+ break;
48+ case address_mode_t::none:
49+ reg.src = nullptr;
50+ break;
51+ default:
52+ assert(false && "Unknown source addressing");
53+ }
54+
55+ switch (inst.destination.mode)
56+ {
57+ case address_mode_t::offset_indirect_fp:
58+ reg.dest = reinterpret_cast<pointer_t>(reinterpret_cast<uint8_t *>(reg.stack.peek_frame()->base()) + inst.destination.register1);
59+ break;
60+ case address_mode_t::offset_indirect_mp:
61+ reg.dest = reinterpret_cast<pointer_t>(reinterpret_cast<uint8_t *>(reg.mp_base->allocation()) + inst.destination.register1);
62+ break;
63+ case address_mode_t::offset_double_indirect_fp:
64+ {
65+ const auto frame_offset = *reinterpret_cast<std::size_t *>(reinterpret_cast<uint8_t *>(reg.stack.peek_frame()->base()) + inst.destination.register1);
66+ reg.dest = reinterpret_cast<pointer_t>(frame_offset + inst.destination.register2);
67+ break;
68+ }
69+ case address_mode_t::offset_double_indirect_mp:
70+ {
71+ const auto mp_offset = *reinterpret_cast<std::size_t *>(reinterpret_cast<uint8_t *>(reg.mp_base->allocation()) + inst.destination.register1);
72+ reg.dest = reinterpret_cast<pointer_t>(mp_offset + inst.destination.register2);
73+ break;
74+ }
75+ case address_mode_t::immediate:
76+ reg.dest = reinterpret_cast<pointer_t>(const_cast<word_t *>(&inst.destination.register1));
77+ break;
78+ case address_mode_t::none:
79+ reg.dest = nullptr;
80+ break;
81+ default:
82+ assert(false && "Unknown destination addressing");
83+ }
84+
85+ switch (inst.middle.mode)
86+ {
87+ case address_mode_middle_t::small_offset_indirect_fp:
88+ reg.mid = reinterpret_cast<pointer_t>(reinterpret_cast<uint8_t *>(reg.stack.peek_frame()->base()) + inst.middle.register1);
89+ break;
90+ case address_mode_middle_t::small_offset_indirect_mp:
91+ reg.mid = reinterpret_cast<pointer_t>(reinterpret_cast<uint8_t *>(reg.mp_base->allocation()) + inst.middle.register1);
92+ break;
93+ case address_mode_middle_t::small_immediate:
94+ reg.mid = reinterpret_cast<pointer_t>(const_cast<word_t *>(&inst.middle.register1));
95+ break;
96+ case address_mode_middle_t::none:
97+ // [SPEC] This is an undocumented expectation but required in many operations (e.g. i++).
98+ reg.mid = reg.dest;
99+ break;
100+ default:
101+ assert(false && "Unknown middle addressing");
102+ }
103+
104+#ifndef NDEBUG
105+ // This is a perf critical function so logging is only available in debug builds
106+ if (debug::is_component_tracing_enabled<debug::component_trace_t::addressing>())
107+ {
108+ debug::log_msg(
109+ debug::component_trace_t::addressing,
110+ debug::log_level_t::debug,
111+ "decode: registers: %d (%#" PRIxPTR " %#" PRIxPTR " %#" PRIxPTR ")\n",
112+ inst.opcode,
113+ reg.src,
114+ reg.mid,
115+ reg.dest);
116+ }
117+#endif
118+ }
119+
120+ std::atomic<uint32_t> thread_id_counter(1);
121+
122+ uint32_t get_unique_thread_id()
123+ {
124+ return thread_id_counter.fetch_add(1);
125+ }
126+}
127+
128+vm_registers_t::vm_registers_t(
129+ const vm_thread_t &thread,
130+ vm_module_ref_t &entry)
131+ : current_thread_state{ vm_thread_state_t::ready }
132+ , module_ref{ &entry }
133+ , mp_base{ entry.mp_base }
134+ , thread{ thread }
135+ , pc{ entry.module->header.entry_pc }
136+ , stack{ entry.module->header.stack_extent }
137+ , src{ nullptr }
138+ , mid{ nullptr }
139+ , dest{ nullptr }
140+{
141+ // Add a reference to the module ref and MP register
142+ if (mp_base != nullptr)
143+ mp_base->add_ref();
144+
145+ module_ref->add_ref();
146+}
147+
148+vm_registers_t::~vm_registers_t()
149+{
150+ dec_ref_count_and_free(mp_base);
151+ debug::assign_debug_pointer(&mp_base);
152+
153+ dec_ref_count_and_free(module_ref);
154+ debug::assign_debug_pointer(&module_ref);
155+}
156+
157+std::shared_ptr<const type_descriptor_t> vm_thread_t::type_desc()
158+{
159+ return intrinsic_type_desc::type<vm_thread_t>();
160+}
161+
162+vm_thread_t::vm_thread_t(
163+ vm_module_ref_t &entry,
164+ uint32_t parent_thread_id)
165+ : vm_alloc_t(vm_thread_t::type_desc())
166+ , _parent_thread_id{ parent_thread_id }
167+ , _registers{ *this, entry }
168+ , _thread_id{ get_unique_thread_id() }
169+{
170+ assert(_parent_thread_id != _thread_id);
171+ assert(static_cast<std::size_t>(_registers.pc) < entry.code_section.size());
172+
173+ // Set up the stack
174+ const auto entry_type = entry.module->header.entry_type;
175+ auto frame_type = entry.type_section[entry_type];
176+ _registers.stack.alloc_frame(frame_type);
177+
178+ // Pushing the initial frame sets the FP register
179+ _registers.stack.push_frame();
180+
181+ debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "init: vm thread: %d %d\n", _thread_id, _parent_thread_id);
182+}
183+
184+vm_thread_t::vm_thread_t(
185+ vm_module_ref_t &entry,
186+ const vm_frame_t &initial_frame,
187+ vm_pc_t start_pc,
188+ uint32_t parent_thread_id)
189+ : vm_alloc_t(vm_thread_t::type_desc())
190+ , _parent_thread_id{ parent_thread_id }
191+ , _registers{ *this, entry }
192+ , _thread_id{ get_unique_thread_id() }
193+{
194+ _registers.pc = start_pc;
195+
196+ assert(_parent_thread_id != _thread_id);
197+ assert(static_cast<std::size_t>(_registers.pc) < entry.code_section.size());
198+
199+ // Set the stack
200+ auto frame_type = initial_frame.frame_type;
201+ _registers.stack.alloc_frame(frame_type);
202+
203+ // Pushing the initial frame sets the FP register
204+ auto current_frame = _registers.stack.push_frame();
205+
206+ // Copy over the frame into this thread - pass arguments to the thread
207+ current_frame->copy_frame_contents(initial_frame);
208+
209+ debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "init: vm thread: %d %d\n", _thread_id, _parent_thread_id);
210+}
211+
212+vm_thread_t::~vm_thread_t()
213+{
214+ debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "destroy: vm thread: %d %d\n", _thread_id, _parent_thread_id);
215+}
216+
217+vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, uint32_t quanta)
218+{
219+ _registers.current_thread_state = vm_thread_state_t::running;
220+ assert(_registers.pc != runtime_constants::invalid_program_counter);
221+
222+ auto execution_duration = uint32_t{ 0 };
223+
224+ try
225+ {
226+ for (; execution_duration < quanta; ++execution_duration)
227+ {
228+ if (_registers.stack.peek_frame() == nullptr)
229+ {
230+ debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "exit: vm thread: stack exhausted: %d\n", _thread_id);
231+ _registers.current_thread_state = vm_thread_state_t::empty_stack;
232+ break;
233+ }
234+
235+ const auto &code_section = _registers.module_ref->code_section;
236+ assert(static_cast<std::size_t>(_registers.pc) < code_section.size());
237+
238+ const auto &inst = code_section[_registers.pc].op;
239+ decode_address(inst, _registers);
240+ _registers.pc++;
241+ vm_exec_table[static_cast<std::size_t>(inst.opcode)](_registers, virtual_machine);
242+
243+ if (_registers.current_thread_state != vm_thread_state_t::running)
244+ break;
245+ }
246+ }
247+ catch (const unhandled_user_exception &uue)
248+ {
249+ // [TODO] Store the exception
250+ debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "broken: vm thread: unhandled: >>%s<< %d >>%s<<\n", uue.exception_id, uue.program_counter, uue.module_name);
251+ _registers.current_thread_state = vm_thread_state_t::broken;
252+ }
253+ catch (const vm_user_exception &ue)
254+ {
255+ // [TODO] Store the exception
256+ debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "broken: vm thread: >>%s<<\n", ue.what());
257+ _registers.current_thread_state = vm_thread_state_t::broken;
258+ }
259+
260+ // Log the execution result
261+ if (debug::is_component_tracing_enabled<debug::component_trace_t::thread>())
262+ {
263+ debug::log_msg(
264+ debug::component_trace_t::thread,
265+ debug::log_level_t::debug,
266+ "status: vm thread: execute: %d %d %d %d\n",
267+ _thread_id,
268+ execution_duration,
269+ quanta,
270+ _registers.current_thread_state);
271+ }
272+
273+ // If the thread is still in the running state transition it back to ready.
274+ if (_registers.current_thread_state == vm_thread_state_t::running)
275+ _registers.current_thread_state = vm_thread_state_t::ready;
276+
277+ return _registers.current_thread_state;
278+}
279+
280+vm_thread_state_t vm_thread_t::get_state() const
281+{
282+ return _registers.current_thread_state;
283+}
284+
285+uint32_t vm_thread_t::get_thread_id() const
286+{
287+ return _thread_id;
288+}
289+
290+uint32_t vm_thread_t::get_parent_thread_id() const
291+{
292+ return _parent_thread_id;
293+}
294+
295+const vm_registers_t& vm_thread_t::get_registers() const
296+{
297+ return _registers;
298+}