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
17using namespace disvm;
18using namespace disvm::runtime;
19
20namespace
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
128vm_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
148vm_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
157std::shared_ptr<const type_descriptor_t> vm_thread_t::type_desc()
158{
159 return intrinsic_type_desc::type<vm_thread_t>();
160}
161
162vm_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
184vm_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
212vm_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
217vm_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
280vm_thread_state_t vm_thread_t::get_state() const
281{
282 return _registers.current_thread_state;
283}
284
285uint32_t vm_thread_t::get_thread_id() const
286{
287 return _thread_id;
288}
289
290uint32_t vm_thread_t::get_parent_thread_id() const
291{
292 return _parent_thread_id;
293}
294
295const vm_registers_t& vm_thread_t::get_registers() const
296{
297 return _registers;
298}