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

Unified · Split

src/vm/execution_table.cpp+1814−0
@@ -0,0 +1,1814 @@
1//
2// Dis VM
3// File: execution_table.cpp
4// Author: arr
5//
6
7#include <type_traits>
8#include <numeric>
9#include <algorithm>
10#include <array>
11#include <cstdint>
12#include <limits>
13#include <random>
14#include <disvm.h>
15#include <vm_memory.h>
16#include <opcodes.h>
17#include <utils.h>
18#include <exceptions.h>
19#include <debug.h>
20#include "execution_table.h"
21
22using namespace disvm;
23using namespace disvm::runtime;
24
25namespace
26{
27 //
28 // Private VM primitive data type definitions
29 //
30
31 using uword_t = uint32_t; // unsigned word_t
32 using ubig_t = uint64_t; // unsigned big_t
33
34 template<typename ValueType>
35 ValueType& vt_ref(pointer_t vt)
36 {
37 assert(vt != nullptr);
38 return *reinterpret_cast<ValueType *>(vt);
39 }
40
41 template<typename AllocType>
42 typename std::enable_if<std::is_base_of<vm_alloc_t, AllocType>::value, AllocType *>::type
43 at_val(pointer_t at)
44 {
45 assert(at != nullptr);
46
47 return vm_alloc_t::from_allocation<AllocType>(*reinterpret_cast<pointer_t *>(at));
48 }
49
50 template<>
51 vm_alloc_t *at_val<vm_alloc_t>(pointer_t at)
52 {
53 assert(at != nullptr);
54
55 return vm_alloc_t::from_allocation(*reinterpret_cast<pointer_t *>(at));
56 }
57
58 pointer_t &pt_ref(pointer_t pt)
59 {
60 assert(pt != nullptr);
61 return *reinterpret_cast<pointer_t *>(pt);
62 }
63
64#define EXEC_DECL(inst_name) void inst_name(vm_registers_t &r, vm_t& vm)
65
66 EXEC_DECL(notimpl) { assert(false && "Instruction not implemented"); throw vm_system_exception{ "Instruction not implemented" }; }
67
68 EXEC_DECL(invalid) { throw vm_system_exception{ "Illegal Dis VM instruction" }; }
69
70 // [SPEC] Documented as 'Clear exception stack' and defined to be used at the discretion of the VM implementation.
71 // Implemented as a 'spare slot' in Inferno
72 EXEC_DECL(eclr) { assert(false && "The 'eclr' instruction is not expected to be used"); }
73
74 //
75 // Bit-wise operations
76 //
77
78 EXEC_DECL(andb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) & vt_ref<byte_t>(r.mid); }
79 EXEC_DECL(andw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) & vt_ref<word_t>(r.mid); }
80 EXEC_DECL(andl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) & vt_ref<big_t>(r.mid); }
81 EXEC_DECL(orb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) | vt_ref<byte_t>(r.mid); }
82 EXEC_DECL(orw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) | vt_ref<word_t>(r.mid); }
83 EXEC_DECL(orl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) | vt_ref<big_t>(r.mid); }
84 EXEC_DECL(xorb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) ^ vt_ref<byte_t>(r.mid); }
85 EXEC_DECL(xorw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) ^ vt_ref<word_t>(r.mid); }
86 EXEC_DECL(xorl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) ^ vt_ref<big_t>(r.mid); }
87
88 //
89 // Bit-shift operations
90 //
91
92 EXEC_DECL(shlb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.mid) << vt_ref<byte_t>(r.src); }
93 EXEC_DECL(shlw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.mid) << vt_ref<word_t>(r.src); }
94 EXEC_DECL(shll) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) << vt_ref<big_t>(r.src); }
95 EXEC_DECL(shrb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.mid) >> vt_ref<byte_t>(r.src); }
96 EXEC_DECL(shrw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.mid) >> vt_ref<word_t>(r.src); }
97 EXEC_DECL(shrl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) >> vt_ref<big_t>(r.src); }
98 EXEC_DECL(lsrw) { vt_ref<word_t>(r.dest) = vt_ref<uword_t>(r.mid) >> vt_ref<word_t>(r.src); }
99 EXEC_DECL(lsrl) { vt_ref<big_t>(r.dest) = vt_ref<ubig_t>(r.mid) >> vt_ref<big_t>(r.src); }
100
101 //
102 // Conversion operations
103 //
104
105 EXEC_DECL(cvtbw) { vt_ref<word_t>(r.dest) = std::numeric_limits<byte_t>::max() & vt_ref<byte_t>(r.src); }
106 EXEC_DECL(cvtwb) { vt_ref<byte_t>(r.dest) = std::numeric_limits<byte_t>::max() & vt_ref<word_t>(r.src); }
107 EXEC_DECL(cvtwl) { vt_ref<big_t>(r.dest) = std::numeric_limits<word_t>::max() & vt_ref<word_t>(r.src); }
108 EXEC_DECL(cvtlw) { vt_ref<word_t>(r.dest) = std::numeric_limits<word_t>::max() & vt_ref<big_t>(r.src); }
109 EXEC_DECL(cvtrf) { vt_ref<short_real_t>(r.dest) = static_cast<short_real_t>(vt_ref<real_t>(r.src)); }
110 EXEC_DECL(cvtfr) { vt_ref<real_t>(r.dest) = static_cast<real_t>(vt_ref<short_real_t>(r.src)); }
111 EXEC_DECL(cvtws) { vt_ref<short_word_t>(r.dest) = std::numeric_limits<short_word_t>::max() & vt_ref<word_t>(r.src); }
112 EXEC_DECL(cvtsw) { vt_ref<word_t>(r.dest) = std::numeric_limits<short_word_t>::max() & vt_ref<short_word_t>(r.src); }
113 EXEC_DECL(cvtlf) { vt_ref<real_t>(r.dest) = static_cast<real_t>(vt_ref<big_t>(r.src)); }
114 EXEC_DECL(cvtfl)
115 {
116 auto f = vt_ref<real_t>(r.src);
117 f = f < 0 ? (f - 0.5) : (f + 0.5);
118 vt_ref<big_t>(r.dest) = static_cast<big_t>(f);
119 }
120
121 EXEC_DECL(cvtxf) { vt_ref<real_t>(r.dest) = static_cast<real_t>(vt_ref<word_t>(r.src)) * vt_ref<real_t>(r.mid); }
122 EXEC_DECL(cvtfx)
123 {
124 auto f = vt_ref<real_t>(r.src) * vt_ref<real_t>(r.mid);
125 f = f < 0 ? (f - 0.5) : (f + 0.5);
126 vt_ref<word_t>(r.dest) = static_cast<word_t>(f);
127 }
128
129 EXEC_DECL(cvtwf) { vt_ref<real_t>(r.dest) = static_cast<real_t>(vt_ref<word_t>(r.src)); }
130 EXEC_DECL(cvtfw)
131 {
132 auto f = vt_ref<real_t>(r.src);
133 f = f < 0 ? (f - 0.5) : (f + 0.5);
134 vt_ref<word_t>(r.dest) = static_cast<word_t>(f);
135 }
136
137 EXEC_DECL(cvtxx)
138 {
139 const auto p = vt_ref<word_t>(r.mid);
140 auto res = static_cast<big_t>(vt_ref<word_t>(r.src));
141 if (p >= 0)
142 res <<= p;
143 else
144 res >>= (-p);
145
146 vt_ref<word_t>(r.dest) = static_cast<word_t>(res);
147 }
148
149 EXEC_DECL(cvtca)
150 {
151 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
152
153 auto str = at_val<vm_string_t>(r.src);
154 auto new_array = new vm_array_t(str);
155 pt_ref(r.dest) = new_array->allocation();
156 }
157
158 EXEC_DECL(cvtac)
159 {
160 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
161
162 auto str = pointer_t{};
163 auto arr = at_val<vm_array_t>(r.src);
164 if (arr != nullptr)
165 {
166 if (arr->get_element_type() != intrinsic_type_desc::type<byte_t>())
167 throw vm_user_exception{ "Invalid array element type for string conversion" };
168
169 auto new_string = new vm_string_t(arr->get_length(), reinterpret_cast<uint8_t *>(arr->at(0)));
170 str = new_string->allocation();
171 }
172
173 pt_ref(r.dest) = str;
174 }
175
176 EXEC_DECL(cvtcw)
177 {
178 auto result = word_t{ 0 };
179 auto str = at_val<vm_string_t>(r.src);
180 if (str != nullptr)
181 result = static_cast<word_t>(::strtol(str->str(), nullptr, 10));
182
183 vt_ref<word_t>(r.dest) = result;
184 }
185
186 EXEC_DECL(cvtwc)
187 {
188 // String represents the largest possible string from a word.
189 auto buffer = std::array<char, sizeof("�2147483648")>{};
190
191 const auto w = vt_ref<word_t>(r.src);
192 const auto len = ::sprintf(buffer.data(), "%d", w);
193 assert(static_cast<std::size_t>(len) <= buffer.size());
194
195 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
196 auto str = at_val<vm_string_t>(r.dest);
197 dec_ref_count_and_free(str);
198
199 pt_ref(r.dest) = new_string->allocation();
200 }
201
202 EXEC_DECL(cvtcf)
203 {
204 auto result = real_t{ 0.0 };
205 auto str = at_val<vm_string_t>(r.src);
206 if (str != nullptr)
207 result = static_cast<real_t>(::strtod(str->str(), nullptr));
208
209 vt_ref<real_t>(r.dest) = result;
210 }
211
212 EXEC_DECL(cvtfc)
213 {
214 // String represents the largest possible string from a double with scientific notation.
215 auto buffer = std::array<char, sizeof("-2.2250738585072014e-308")>{};
216
217 const auto real = vt_ref<real_t>(r.src);
218 const auto len = ::sprintf(buffer.data(), "%g", real);
219 assert(static_cast<std::size_t>(len) <= buffer.size());
220
221 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
222
223 auto str = at_val<vm_string_t>(r.dest);
224 pt_ref(r.dest) = new_string->allocation();
225 dec_ref_count_and_free(str);
226 }
227
228 EXEC_DECL(cvtcl)
229 {
230 auto result = big_t{ 0 };
231 auto str = at_val<vm_string_t>(r.src);
232 if (str != nullptr)
233 result = static_cast<big_t>(::strtoll(str->str(), nullptr, 10));
234
235 vt_ref<big_t>(r.dest) = result;
236 }
237
238 EXEC_DECL(cvtlc)
239 {
240 // String represents the largest possible string from a 64-bit integer.
241 auto buffer = std::array<char, sizeof("-9223372036854775808")>{};
242
243 const auto b = vt_ref<big_t>(r.src);
244 const auto len = ::sprintf(buffer.data(), "%lld", b);
245 assert(static_cast<std::size_t>(len) <= buffer.size());
246
247 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
248
249 auto str = at_val<vm_string_t>(r.dest);
250 pt_ref(r.dest) = new_string->allocation();
251 dec_ref_count_and_free(str);
252 }
253
254 //
255 // Arithmetic operations
256 //
257
258 EXEC_DECL(negf) { vt_ref<real_t>(r.dest) = -vt_ref<real_t>(r.src); }
259 EXEC_DECL(addb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) + vt_ref<byte_t>(r.mid); }
260 EXEC_DECL(addw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) + vt_ref<word_t>(r.mid); }
261 EXEC_DECL(addl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) + vt_ref<big_t>(r.mid); }
262 EXEC_DECL(addf) { vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.src) + vt_ref<real_t>(r.mid); }
263 EXEC_DECL(subb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.mid) - vt_ref<byte_t>(r.src); }
264 EXEC_DECL(subw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.mid) - vt_ref<word_t>(r.src); }
265 EXEC_DECL(subl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) - vt_ref<big_t>(r.src); }
266 EXEC_DECL(subf) { vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.mid) - vt_ref<real_t>(r.src); }
267 EXEC_DECL(mulb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) * vt_ref<byte_t>(r.mid); }
268 EXEC_DECL(mulw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) * vt_ref<word_t>(r.mid); }
269 EXEC_DECL(mull) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) * vt_ref<big_t>(r.mid); }
270 EXEC_DECL(mulf) { vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.src) * vt_ref<real_t>(r.mid); }
271
272 template<typename PrimitiveType>
273 void _div(vm_registers_t &r)
274 {
275 const auto denom = vt_ref<PrimitiveType>(r.src);
276 if (denom == PrimitiveType{ 0 })
277 throw divide_by_zero{};
278
279 vt_ref<PrimitiveType>(r.dest) = vt_ref<PrimitiveType>(r.mid) / denom;
280 }
281
282 EXEC_DECL(divb) { _div<byte_t>(r); }
283 EXEC_DECL(divw) { _div<word_t>(r); }
284 EXEC_DECL(divf) { _div<real_t>(r); }
285 EXEC_DECL(divl) { _div<big_t>(r); }
286
287 template<typename PrimitiveType>
288 void _mod(vm_registers_t &r)
289 {
290 const auto denom = vt_ref<PrimitiveType>(r.src);
291 if (denom == PrimitiveType{ 0 })
292 throw divide_by_zero{};
293
294 vt_ref<PrimitiveType>(r.dest) = vt_ref<PrimitiveType>(r.mid) % denom;
295 }
296
297 EXEC_DECL(modb) { _mod<byte_t>(r); }
298 EXEC_DECL(modw) { _mod<word_t>(r); }
299 EXEC_DECL(modl) { _mod<big_t>(r); }
300
301 // [SPEC] 'Exponent' op code
302 // Undocumented: (http://www.vitanuova.com/inferno/papers/dis.html)
303 // Limbo reference: (http://www.vitanuova.com/inferno/papers/addendum.pdf)
304 //
305 // Inferno implementation: (libinterp/xec.c)
306 // src1 = power
307 // src2 = base
308 //
309 // exp[w|l|f] src1 src2 dst
310 // dst = src2 'raised to' src1
311 //
312 // Result type matches the type of base. Power should be type 'word'.
313 template<typename PrimitiveType>
314 void _exp(vm_registers_t &r)
315 {
316 auto base = vt_ref<PrimitiveType>(r.mid);
317 auto power = vt_ref<word_t>(r.src);
318
319 auto inverse = false;
320 if (power < 0)
321 {
322 power = (-power);
323 inverse = true;
324 }
325
326 auto result = PrimitiveType{ 1 };
327 for (;;)
328 {
329 if ((power & 1) != 0)
330 result *= base;
331
332 power >>= 1;
333 if (power == 0)
334 break;
335
336 base *= base;
337 }
338
339 if (inverse)
340 result = (1 / result);
341
342 vt_ref<PrimitiveType>(r.dest) = result;
343 }
344
345 EXEC_DECL(expw) { _exp<word_t>(r); }
346 EXEC_DECL(expl) { _exp<big_t>(r); }
347 EXEC_DECL(expf) { _exp<real_t>(r); }
348
349 // [SPEC] 'Fixed point' op codes
350 // Undocumented: (http://www.vitanuova.com/inferno/papers/dis.html)
351 // Limbo reference: (http://www.vitanuova.com/inferno/papers/addendum.pdf)
352 EXEC_DECL(mulx)
353 {
354 const auto m = big_t{ vt_ref<word_t>(r.mid) };
355 const auto s = big_t{ vt_ref<word_t>(r.src) };
356 auto result = m * s;
357
358 const auto p = *reinterpret_cast<word_t *>(r.stack.peek_frame()->fixed_point_register_2());
359 if (p >= 0)
360 result <<= p;
361 else
362 result >>= (-p);
363
364 vt_ref<word_t>(r.dest) = static_cast<word_t>(result);
365 }
366
367 EXEC_DECL(divx)
368 {
369 auto denom = big_t{ vt_ref<word_t>(r.mid) };
370 const auto p = *reinterpret_cast<word_t *>(r.stack.peek_frame()->fixed_point_register_2());
371 if (p >= 0)
372 denom <<= p;
373 else
374 denom >>= (-p);
375
376 const auto numerator = big_t{ vt_ref<word_t>(r.src) };
377 auto result = denom / numerator;
378
379 vt_ref<word_t>(r.dest) = static_cast<word_t>(result);
380 }
381
382 //
383 // Move operations
384 //
385
386 template<typename PrimitiveType>
387 void _mov_(pointer_t dest, pointer_t src, const type_descriptor_t *)
388 {
389 vt_ref<PrimitiveType>(dest) = vt_ref<PrimitiveType>(src);
390 }
391
392 template<>
393 void _mov_<vm_alloc_t>(pointer_t dest, pointer_t src, const type_descriptor_t *)
394 {
395 auto alloc_prev = at_val<vm_alloc_t>(dest);
396 dec_ref_count_and_free(alloc_prev);
397
398 auto alloc = at_val<vm_alloc_t>(src);
399 if (alloc != nullptr)
400 {
401 alloc->add_ref();
402 pt_ref(dest) = alloc->allocation();
403 }
404 else
405 {
406 pt_ref(dest) = nullptr;
407 }
408 }
409
410 EXEC_DECL(movb) { _mov_<byte_t>(r.dest, r.src, nullptr); }
411 EXEC_DECL(movw) { _mov_<word_t>(r.dest, r.src, nullptr); }
412 EXEC_DECL(movf) { _mov_<real_t>(r.dest, r.src, nullptr); }
413 EXEC_DECL(movl) { _mov_<big_t>(r.dest, r.src, nullptr); }
414 EXEC_DECL(movp) { _mov_<vm_alloc_t>(r.dest, r.src, nullptr); }
415
416 EXEC_DECL(movm)
417 {
418 assert(r.src != nullptr && r.dest != nullptr);
419
420 std::memmove(r.dest, r.src, vt_ref<word_t>(r.mid));
421 }
422
423 EXEC_DECL(movmp)
424 {
425 auto type_id = vt_ref<word_t>(r.mid);
426 const auto &types = r.module_ref->type_section;
427 assert(0 <= type_id && type_id < static_cast<word_t>(types.size()));
428 auto type = types[type_id];
429
430 disvm::runtime::inc_ref_count_in_memory(*type, r.src);
431
432 // Decrement allocated pointers in the destination.
433 destroy_memory(*type, r.dest);
434
435 assert(r.src != nullptr && r.dest != nullptr);
436 std::memmove(r.dest, r.src, type->size_in_bytes);
437 }
438
439 //
440 // Compare and Branch operations
441 //
442
443 EXEC_DECL(bltb) { if (vt_ref<byte_t>(r.src) < vt_ref<byte_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
444 EXEC_DECL(bgtb) { if (vt_ref<byte_t>(r.src) > vt_ref<byte_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
445 EXEC_DECL(bleb) { if (vt_ref<byte_t>(r.src) <= vt_ref<byte_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
446 EXEC_DECL(bgeb) { if (vt_ref<byte_t>(r.src) >= vt_ref<byte_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
447 EXEC_DECL(beqb) { if (vt_ref<byte_t>(r.src) == vt_ref<byte_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
448 EXEC_DECL(bneb) { if (vt_ref<byte_t>(r.src) != vt_ref<byte_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
449
450 EXEC_DECL(bltw) { if (vt_ref<word_t>(r.src) < vt_ref<word_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
451 EXEC_DECL(bgtw) { if (vt_ref<word_t>(r.src) > vt_ref<word_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
452 EXEC_DECL(blew) { if (vt_ref<word_t>(r.src) <= vt_ref<word_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
453 EXEC_DECL(bgew) { if (vt_ref<word_t>(r.src) >= vt_ref<word_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
454 EXEC_DECL(beqw) { if (vt_ref<word_t>(r.src) == vt_ref<word_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
455 EXEC_DECL(bnew) { if (vt_ref<word_t>(r.src) != vt_ref<word_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
456
457 EXEC_DECL(bltf) { if (vt_ref<real_t>(r.src) < vt_ref<real_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
458 EXEC_DECL(bgtf) { if (vt_ref<real_t>(r.src) > vt_ref<real_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
459 EXEC_DECL(blef) { if (vt_ref<real_t>(r.src) <= vt_ref<real_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
460 EXEC_DECL(bgef) { if (vt_ref<real_t>(r.src) >= vt_ref<real_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
461 EXEC_DECL(beqf) { if (vt_ref<real_t>(r.src) == vt_ref<real_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
462 EXEC_DECL(bnef) { if (vt_ref<real_t>(r.src) != vt_ref<real_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
463
464 EXEC_DECL(bltl) { if (vt_ref<big_t>(r.src) < vt_ref<big_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
465 EXEC_DECL(bgtl) { if (vt_ref<big_t>(r.src) > vt_ref<big_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
466 EXEC_DECL(blel) { if (vt_ref<big_t>(r.src) <= vt_ref<big_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
467 EXEC_DECL(bgel) { if (vt_ref<big_t>(r.src) >= vt_ref<big_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
468 EXEC_DECL(beql) { if (vt_ref<big_t>(r.src) == vt_ref<big_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
469 EXEC_DECL(bnel) { if (vt_ref<big_t>(r.src) != vt_ref<big_t>(r.mid)) r.pc = vt_ref<vm_pc_t>(r.dest); }
470
471 // Define case table constants
472 // [SPEC]Table format is partially defined in Dis VM specification
473 // [element count] ([low value (inc)][high value (exc)][program counter])* [fallback program counter]
474 struct case_op_constants
475 {
476 static const auto table_length_index = std::size_t{ 0 };
477 static const auto first_entry_index = std::size_t{ 1 };
478 static const auto entry_low_index = std::size_t{ 0 };
479 static const auto entry_high_index = std::size_t{ 1 };
480 static const auto entry_pc_index = std::size_t{ 2 };
481 static const auto entry_length = std::size_t{ 3 };
482 };
483
484 template<typename PrimitiveType>
485 void _case(vm_registers_t &r)
486 {
487 const auto value = vt_ref<PrimitiveType>(r.src);
488
489 assert(r.dest != nullptr);
490 const auto table = reinterpret_cast<const PrimitiveType *>(r.dest);
491 auto entry_count = table[case_op_constants::table_length_index];
492 assert(entry_count < std::numeric_limits<word_t>::max());
493
494 // Initialize the target PC with the fallback value.
495 // This value is located after all entries in the table.
496 auto target_pc = table[case_op_constants::first_entry_index + (entry_count * case_op_constants::entry_length)];
497
498 // Move to first entry
499 auto current_entry = table + case_op_constants::first_entry_index;
500
501 // Find the matching table entry
502 for (; 0 < entry_count; --entry_count)
503 {
504 // Check if value is in range
505 if (value < current_entry[case_op_constants::entry_low_index] || current_entry[case_op_constants::entry_high_index] <= value)
506 {
507 current_entry += case_op_constants::entry_length;
508 continue;
509 }
510
511 target_pc = current_entry[case_op_constants::entry_pc_index];
512 break;
513 }
514
515 assert(0 <= static_cast<std::size_t>(target_pc) && static_cast<std::size_t>(target_pc) < r.module_ref->code_section.size());
516 r.pc = static_cast<vm_pc_t>(target_pc);
517 }
518
519 EXEC_DECL(casew) { _case<word_t>(r); }
520 EXEC_DECL(casel) { _case<big_t>(r); }
521
522 // [SPEC] The documentation for the 'casec' instruction is a bit sparse
523 // The full semantics are the case table should have string cases lexically
524 // ordered (i.e. e[n] < e[n + 1]).
525 EXEC_DECL(casec)
526 {
527 const auto value = at_val<vm_string_t>(r.src);
528
529 assert(r.dest != nullptr);
530 const auto table = reinterpret_cast<const word_t *>(r.dest);
531 const auto entry_count = table[case_op_constants::table_length_index];
532
533 // Initialize the target PC with the fallback value.
534 // This value is located after all entries in the table.
535 auto target_pc = table[case_op_constants::first_entry_index + (entry_count * case_op_constants::entry_length)];
536
537 // Move to first entry
538 auto current_entry = table + case_op_constants::first_entry_index;
539
540 // Find the matching table entry
541 for (auto entry = word_t{ 0 }; entry < entry_count; ++entry)
542 {
543 auto string_low = at_val<vm_string_t>(reinterpret_cast<pointer_t>(const_cast<word_t *>(current_entry + case_op_constants::entry_low_index)));
544
545 // Compare the value against the low string
546 int result = vm_string_t::compare(value, string_low);
547 if (result != 0)
548 {
549 // If the value compares less than the low string then skip the high string.
550 if (result < 0)
551 {
552 current_entry += case_op_constants::entry_length;
553 continue;
554 }
555
556 // Compare the value against the high string
557 auto string_high = at_val<vm_string_t>(reinterpret_cast<pointer_t>(const_cast<word_t *>(current_entry + case_op_constants::entry_high_index)));
558 if (string_high == nullptr || vm_string_t::compare(value, string_high) != 0)
559 {
560 current_entry += case_op_constants::entry_length;
561 continue;
562 }
563 }
564
565 target_pc = current_entry[case_op_constants::entry_pc_index];
566 break;
567 }
568
569 r.pc = target_pc;
570 }
571
572 //
573 // String operations
574 //
575
576 EXEC_DECL(bltc) { if (vm_string_t::compare(at_val<vm_string_t>(r.src), at_val<vm_string_t>(r.mid)) < 0) r.pc = vt_ref<vm_pc_t>(r.dest); }
577 EXEC_DECL(bgtc) { if (vm_string_t::compare(at_val<vm_string_t>(r.src), at_val<vm_string_t>(r.mid)) > 0) r.pc = vt_ref<vm_pc_t>(r.dest); }
578 EXEC_DECL(blec) { if (vm_string_t::compare(at_val<vm_string_t>(r.src), at_val<vm_string_t>(r.mid)) <= 0) r.pc = vt_ref<vm_pc_t>(r.dest); }
579 EXEC_DECL(bgec) { if (vm_string_t::compare(at_val<vm_string_t>(r.src), at_val<vm_string_t>(r.mid)) >= 0) r.pc = vt_ref<vm_pc_t>(r.dest); }
580 EXEC_DECL(beqc) { if (vm_string_t::compare(at_val<vm_string_t>(r.src), at_val<vm_string_t>(r.mid)) == 0) r.pc = vt_ref<vm_pc_t>(r.dest); }
581 EXEC_DECL(bnec) { if (vm_string_t::compare(at_val<vm_string_t>(r.src), at_val<vm_string_t>(r.mid)) != 0) r.pc = vt_ref<vm_pc_t>(r.dest); }
582
583 EXEC_DECL(lenc)
584 {
585 auto len = word_t{ 0 };
586 auto str = at_val<vm_string_t>(r.src);
587 if (str != nullptr)
588 len = str->get_length();
589
590 vt_ref<word_t>(r.dest) = len;
591 }
592
593 EXEC_DECL(indc)
594 {
595 auto str = at_val<vm_string_t>(r.src);
596 if (str == nullptr)
597 throw dereference_nil{ "Index string" };
598
599 const auto index = vt_ref<word_t>(r.mid);
600 const auto rune = str->get_rune(index);
601
602 vt_ref<rune_t>(r.dest) = rune;
603 }
604
605 // [SPEC] 'Insert Character' op code
606 // Documented: (http://www.vitanuova.com/inferno/papers/dis.html)
607 // insc src1 src2 dst
608 // src1[src2] = dst
609 //
610 // Inferno implementation: (libinterp/string.c)
611 // insc src1 src2 dst
612 // dst[src2] = src1
613 EXEC_DECL(insc)
614 {
615 auto rune = vt_ref<rune_t>(r.src);
616 auto index = vt_ref<word_t>(r.mid);
617 auto str = at_val<vm_string_t>(r.dest);
618 if (str == nullptr)
619 str = new vm_string_t();
620 else if (str->get_ref_count() > 1)
621 str = new vm_string_t(*str, 0, str->get_length());
622
623 // Set the rune at the supplied index.
624 str->set_rune(index, rune);
625
626 auto old_str = at_val<vm_string_t>(r.dest);
627 if (str != old_str)
628 {
629 pt_ref(r.dest) = str->allocation();
630 dec_ref_count_and_free(old_str);
631 }
632 }
633
634 vm_string_t *_addc(vm_string_t *s1, vm_string_t *s2, /* in out */ bool &append_to_s1)
635 {
636 if (s1 == nullptr)
637 {
638 if (s2 == nullptr)
639 return new vm_string_t();
640
641 return new vm_string_t(*s2, 0, s2->get_length());
642 }
643
644 // There are other references to this string so we will not alter it.
645 if (s1->get_ref_count() > 1)
646 append_to_s1 = false;
647
648 if (s2 == nullptr)
649 {
650 if (append_to_s1)
651 return s1;
652
653 return new vm_string_t(*s1, 0, s1->get_length());
654 }
655
656 return (append_to_s1) ? &(s1->append(*s2)) : new vm_string_t(*s1, *s2);
657 }
658
659 // [SPEC] 'Concat strings' op code
660 // Documented: (http://www.vitanuova.com/inferno/papers/dis.html)
661 // addc src1 src2 dst
662 // dst = src1 + src2
663 //
664 // Inferno implementation: (libinterp/string.c)
665 // addc src1 src2 dst
666 // dst = src2 + src1
667 EXEC_DECL(addc)
668 {
669 auto s1 = at_val<vm_string_t>(r.mid);
670 auto s2 = at_val<vm_string_t>(r.src);
671 auto dest_maybe = at_val<vm_string_t>(r.dest);
672
673 // If the source is the same as the destination and the
674 // destination is not null, append the string. This can
675 // change during the concat process.
676 auto append_to_source = dest_maybe != nullptr && s1 == dest_maybe;
677 auto str = _addc(s1, s2, append_to_source);
678
679 if (!append_to_source)
680 {
681 pt_ref(r.dest) = str->allocation();
682 dec_ref_count_and_free(dest_maybe);
683 }
684 }
685
686 EXEC_DECL(slicec)
687 {
688 const auto start = vt_ref<word_t>(r.src);
689 const auto end = vt_ref<word_t>(r.mid);
690 auto str = at_val<vm_string_t>(r.dest);
691
692 if (str != nullptr)
693 {
694 auto new_string = new vm_string_t(*str, start, end);
695 pt_ref(r.dest) = new_string->allocation();
696 }
697 else if (start == 0 && end == 0)
698 {
699 pt_ref(r.dest) = nullptr;
700 }
701 else
702 {
703 throw dereference_nil{ "Slice a nil string" };
704 }
705
706 dec_ref_count_and_free(str);
707 }
708
709 //
710 // Array operations
711 //
712
713 EXEC_DECL(lena)
714 {
715 auto len = word_t{ 0 };
716 auto arr = at_val<vm_array_t>(r.src);
717 if (arr != nullptr)
718 len = arr->get_length();
719
720 vt_ref<word_t>(r.dest) = len;
721 }
722
723 EXEC_DECL(slicea)
724 {
725 const auto begin_index = vt_ref<word_t>(r.src);
726 const auto end_index = vt_ref<word_t>(r.mid);
727 const auto length = end_index - begin_index;
728
729 auto arr = at_val<vm_array_t>(r.dest);
730 if (arr == nullptr)
731 {
732 if (length == 0)
733 return;
734
735 throw vm_user_exception{ "Slice of empty array is invalid" };
736 }
737 else if (begin_index < 0 || arr->get_length() < end_index || length < 0)
738 {
739 throw out_of_range_memory{};
740 }
741
742 auto new_array = new vm_array_t(arr, begin_index, length);
743
744 if (arr->alloc_type->map_in_bytes > 0)
745 vm.get_garbage_collector().track_allocation(new_array);
746
747 pt_ref(r.dest) = new_array->allocation();
748 }
749
750 EXEC_DECL(slicela)
751 {
752 auto src_arr = at_val<vm_array_t>(r.src);
753 if (src_arr == nullptr)
754 return;
755
756 auto dest_arr = at_val<vm_array_t>(r.dest);
757 if (dest_arr == nullptr)
758 throw dereference_nil{ "Slice assignment" };
759
760 const auto begin_index = vt_ref<word_t>(r.mid);
761 dest_arr->copy_from(*src_arr, begin_index);
762 }
763
764 void _index_in(vm_registers_t &r)
765 {
766 auto arr = at_val<vm_array_t>(r.src);
767 if (arr == nullptr)
768 throw dereference_nil{ "Indexing into array" };
769
770 auto index = vt_ref<word_t>(r.dest);
771 if (index < 0 || arr->get_length() <= index)
772 throw out_of_range_memory{ 0, arr->get_length(), index };
773
774 // Return the address, not the value
775 pt_ref(r.mid) = arr->at(index);
776 }
777
778 EXEC_DECL(indb) { _index_in(r); }
779 EXEC_DECL(indw) { _index_in(r); }
780 EXEC_DECL(indf) { _index_in(r); }
781 EXEC_DECL(indl) { _index_in(r); }
782 EXEC_DECL(indx) { _index_in(r); }
783
784 //
785 // List operations
786 //
787
788 EXEC_DECL(lenl)
789 {
790 auto len = word_t{ 0 };
791 auto list = at_val<vm_list_t>(r.src);
792 if (list != nullptr)
793 {
794 assert(list->alloc_type == vm_list_t::type_desc());
795 len = list->get_length();
796 }
797
798 vt_ref<word_t>(r.dest) = len;
799 }
800
801 template<typename PrimitiveType>
802 void _cons(vm_registers_t &r)
803 {
804 auto tail_maybe = at_val<vm_list_t>(r.dest);
805 if (tail_maybe != nullptr)
806 tail_maybe->release();
807
808 auto new_list = new vm_list_t(intrinsic_type_desc::type<PrimitiveType>(), tail_maybe);
809
810 auto value = vt_ref<PrimitiveType>(r.src);
811 vt_ref<PrimitiveType>(new_list->value()) = value;
812
813 pt_ref(r.dest) = new_list->allocation();
814 }
815
816 EXEC_DECL(consb) { _cons<byte_t>(r); }
817 EXEC_DECL(consw) { _cons<word_t>(r); }
818 EXEC_DECL(consf) { _cons<real_t>(r); }
819 EXEC_DECL(consl) { _cons<big_t>(r); }
820
821 EXEC_DECL(consp)
822 {
823 auto tail_maybe = at_val<vm_list_t>(r.dest);
824 if (tail_maybe != nullptr)
825 tail_maybe->release();
826
827 // Create a new list
828 auto new_list = new vm_list_t(intrinsic_type_desc::type<pointer_t>(), tail_maybe);
829 auto alloc = at_val<vm_alloc_t>(r.src);
830 if (alloc != nullptr)
831 {
832 alloc->add_ref();
833 pt_ref(new_list->value()) = alloc->allocation();
834
835 vm.get_garbage_collector().track_allocation(new_list);
836 }
837 else
838 {
839 pt_ref(new_list->value()) = nullptr;
840 }
841
842 // Return the list
843 pt_ref(r.dest) = new_list->allocation();
844 }
845
846 EXEC_DECL(consmp)
847 {
848 auto type_id = vt_ref<word_t>(r.mid);
849 const auto &types = r.module_ref->type_section;
850 assert(0 <= type_id && type_id < static_cast<word_t>(types.size()));
851 auto type = types[type_id];
852
853 // Increment the ref count for element content
854 inc_ref_count_in_memory(*type, r.src);
855
856 auto tail_maybe = at_val<vm_list_t>(r.dest);
857 if (tail_maybe != nullptr)
858 tail_maybe->release();
859
860 // Create a new list
861 auto new_list = new vm_list_t(type, tail_maybe);
862 auto destination = new_list->value();
863 std::memmove(destination, r.src, type->size_in_bytes);
864
865 if (type->map_in_bytes > 0)
866 vm.get_garbage_collector().track_allocation(new_list);
867
868 // Return the list
869 pt_ref(r.dest) = new_list->allocation();
870 }
871
872 template<typename PrimitiveType>
873 void _head(vm_registers_t &r)
874 {
875 auto head = at_val<vm_list_t>(r.src);
876 if (head == nullptr)
877 throw dereference_nil{ "Head of list" };
878
879 assert(head->get_element_type() == intrinsic_type_desc::type<PrimitiveType>());
880 auto head_value = head->value();
881 vt_ref<PrimitiveType>(r.dest) = vt_ref<PrimitiveType>(head_value);
882 }
883
884 EXEC_DECL(headb) { _head<byte_t>(r); }
885 EXEC_DECL(headw) { _head<word_t>(r); }
886 EXEC_DECL(headf) { _head<real_t>(r); }
887 EXEC_DECL(headl) { _head<big_t>(r); }
888
889 EXEC_DECL(headp)
890 {
891 auto list = at_val<vm_list_t>(r.src);
892 if (list == nullptr)
893 throw dereference_nil{ "Head of list" };
894
895 r.src = list->value();
896 movp(r, vm);
897 }
898
899 EXEC_DECL(headmp)
900 {
901 auto list = at_val<vm_list_t>(r.src);
902 if (list == nullptr)
903 throw dereference_nil{ "Head of list" };
904
905 r.src = list->value();
906 movmp(r, vm);
907 }
908
909 EXEC_DECL(tail)
910 {
911 auto list = at_val<vm_list_t>(r.src);
912 if (list == nullptr)
913 throw dereference_nil{ "Tail of list" };
914
915 assert(list->alloc_type == vm_list_t::type_desc());
916 auto tail = pointer_t{};
917
918 auto tail_maybe = list->get_tail();
919 if (tail_maybe != nullptr)
920 tail = tail_maybe->allocation();
921
922 // Use the address of the local variable.
923 r.src = reinterpret_cast<pointer_t>(&tail);
924 movp(r, vm);
925 }
926
927 //
928 // Channel operations
929 //
930
931 namespace
932 {
933 void _channel_movm(pointer_t dest, pointer_t src, const type_descriptor_t *type)
934 {
935 assert(type != nullptr);
936 std::memmove(dest, src, type->size_in_bytes);
937 }
938
939 void _channel_movmp(pointer_t dest, pointer_t src, const type_descriptor_t *type)
940 {
941 assert(type != nullptr);
942 destroy_memory(*type, dest);
943
944 inc_ref_count_in_memory(*type, src);
945 std::memmove(dest, src, type->size_in_bytes);
946 }
947
948 // [SPEC] 'Create new channel' op codes
949 // Documented: (http://www.vitanuova.com/inferno/papers/dis.html)
950 // As documented the src2 register is not used, however the Inferno
951 // implementation does use the src2 register. It is checked against
952 // the dst register and if they are not equal the src2 register
953 // contains the internal buffer size of the channel.
954 //
955 // See Inferno implementation: (libinterp/xec.c)
956 void _newc_(
957 vm_registers_t &r,
958 const vm_t &vm,
959 std::shared_ptr<const type_descriptor_t> type_desc,
960 std::function<void(pointer_t, pointer_t, const type_descriptor_t *)> transfer)
961 {
962 auto buffer_len = word_t{ 0 };
963 if (r.mid != r.dest)
964 {
965 buffer_len = vt_ref<word_t>(r.mid);
966 if (buffer_len < 0)
967 throw out_of_range_memory{};
968 }
969
970 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
971 auto new_channel = new vm_channel_t{ type_desc, transfer, buffer_len };
972
973 if (type_desc->map_in_bytes > 0)
974 vm.get_garbage_collector().track_allocation(new_channel);
975
976 pt_ref(r.dest) = new_channel->allocation();
977 }
978 }
979
980 EXEC_DECL(newcb) { _newc_(r, vm, intrinsic_type_desc::type<byte_t>(), _mov_<byte_t>); }
981 EXEC_DECL(newcw) { _newc_(r, vm, intrinsic_type_desc::type<word_t>(), _mov_<word_t>); }
982 EXEC_DECL(newcf) { _newc_(r, vm, intrinsic_type_desc::type<real_t>(), _mov_<real_t>); }
983 EXEC_DECL(newcl) { _newc_(r, vm, intrinsic_type_desc::type<big_t>(), _mov_<big_t>); }
984 EXEC_DECL(newcp) { _newc_(r, vm, intrinsic_type_desc::type<pointer_t>(), _mov_<vm_alloc_t>); }
985
986 EXEC_DECL(newcm)
987 {
988 auto memory_size = vt_ref<word_t>(r.src);
989 auto channel_data_type = type_descriptor_t::create(memory_size);
990
991 _newc_(r, vm, channel_data_type, _channel_movm);
992 }
993
994 EXEC_DECL(newcmp)
995 {
996 auto type_id = vt_ref<word_t>(r.src);
997 const auto &types = r.module_ref->type_section;
998 assert(0 <= type_id && type_id < static_cast<word_t>(types.size()));
999 auto type = types[type_id];
1000
1001 _newc_(r, vm, type, _channel_movmp);
1002 }
1003
1004 EXEC_DECL(send)
1005 {
1006 auto channel = at_val<vm_channel_t>(r.dest);
1007 if (channel == nullptr)
1008 throw dereference_nil{ "Send to channel" };
1009
1010 const auto current_thread_id = r.thread.get_thread_id();
1011
1012 assert(!r.request_mutex.pending_request && "There should be no pending request for the thread");
1013 r.request_mutex.pending_request = true;
1014
1015 assert(r.current_thread_state == vm_thread_state_t::running);
1016 r.current_thread_state = vm_thread_state_t::blocked_sending;
1017
1018 // Setup the request
1019 auto request = vm_channel_request_t{ current_thread_id, r.request_mutex };
1020 request.data = r.src;
1021 request.request_handled_callback = [&vm, &r, current_thread_id](const vm_channel_t &)
1022 {
1023 assert(!r.request_mutex.pending_request && r.current_thread_state == vm_thread_state_t::blocked_sending);
1024 vm.get_scheduler_control().enqueue_blocked_thread(current_thread_id);
1025 };
1026
1027 if (channel->send_data(request))
1028 r.current_thread_state = vm_thread_state_t::running;
1029 }
1030
1031 EXEC_DECL(recv)
1032 {
1033 auto channel = at_val<vm_channel_t>(r.src);
1034 if (channel == nullptr)
1035 throw dereference_nil{ "Receive from channel" };
1036
1037 const auto current_thread_id = r.thread.get_thread_id();
1038
1039 assert(!r.request_mutex.pending_request && "There should be no pending request for the thread");
1040 r.request_mutex.pending_request = true;
1041
1042 assert(r.current_thread_state == vm_thread_state_t::running);
1043 r.current_thread_state = vm_thread_state_t::blocked_receiving;
1044
1045 // Setup the request
1046 auto request = vm_channel_request_t { current_thread_id, r.request_mutex };
1047 request.data = r.dest;
1048 request.request_handled_callback = [&vm, &r, current_thread_id](const vm_channel_t &)
1049 {
1050 assert(!r.request_mutex.pending_request && r.current_thread_state == vm_thread_state_t::blocked_receiving);
1051 vm.get_scheduler_control().enqueue_blocked_thread(current_thread_id);
1052 };
1053
1054 if (channel->receive_data(request))
1055 r.current_thread_state = vm_thread_state_t::running;
1056 }
1057
1058 namespace
1059 {
1060 std::minstd_rand alt_channel_selector;
1061
1062 word_t cancel_outstanding_channel_requests(uint32_t request_thread_id, vm_alt_stack_layout_t *alt, const vm_channel_t &handled_channel)
1063 {
1064 auto alloc = handled_channel.allocation();
1065
1066 auto handled_index = word_t{ -1 };
1067 const auto channel_count = (alt->send_count + alt->receive_count);
1068 for (auto i = word_t{ 0 }; i < channel_count; ++i)
1069 {
1070 auto ch_pointer = reinterpret_cast<pointer_t>(alt->channels[i].alloc);
1071 if (ch_pointer == alloc)
1072 {
1073 assert(handled_index == -1 && "Index should only be set once");
1074 handled_index = i;
1075 continue;
1076 }
1077
1078 auto ch = at_val<vm_channel_t>(reinterpret_cast<pointer_t>(&ch_pointer));
1079 ch->cancel_request(request_thread_id);
1080 }
1081
1082 assert(handled_index != -1 && "Index should be set");
1083 return handled_index;
1084 }
1085
1086 void exec_alt(vm_registers_t &r, const vm_t &vm, const bool blocking)
1087 {
1088 auto alt = reinterpret_cast<vm_alt_stack_layout_t *>(r.src);
1089 assert(alt != nullptr);
1090
1091 const auto current_thread_id = r.thread.get_thread_id();
1092
1093 assert(!r.request_mutex.pending_request && "There should be no pending request for the thread");
1094 r.request_mutex.pending_request = true;
1095
1096 assert(r.current_thread_state == vm_thread_state_t::running);
1097 r.current_thread_state = vm_thread_state_t::blocked_in_alt;
1098
1099 // Create request callback
1100 auto alt_request_handled_callback = [&vm, &r, alt, current_thread_id](const vm_channel_t &handled_channel)
1101 {
1102 // Cancel all non-handled requests and determine the
1103 // index of the channel that handled this request.
1104 auto handled_index = cancel_outstanding_channel_requests(current_thread_id, alt, handled_channel);
1105
1106 // Set the index of the channel that handled the request
1107 vt_ref<word_t>(r.dest) = handled_index;
1108
1109 assert(!r.request_mutex.pending_request && r.current_thread_state == vm_thread_state_t::blocked_in_alt);
1110 vm.get_scheduler_control().enqueue_blocked_thread(current_thread_id);
1111 };
1112
1113 const auto channel_count = (alt->send_count + alt->receive_count);
1114 assert(channel_count > 0);
1115
1116 // Generate a random channel index sequence.
1117 auto random_idx_sequence = std::vector<word_t>(channel_count);
1118 std::iota(std::begin(random_idx_sequence), std::end(random_idx_sequence), 0);
1119 std::shuffle(std::begin(random_idx_sequence), std::end(random_idx_sequence), alt_channel_selector);
1120
1121 auto requests_queued = uint32_t{ 0 };
1122 auto handled = bool{};
1123 for (auto rnd_idx : random_idx_sequence)
1124 {
1125 auto &channel_stack_layout = alt->channels[rnd_idx];
1126 if (channel_stack_layout.alloc == runtime_constants::nil)
1127 throw dereference_nil{ "Alt channel" };
1128
1129 auto channel = at_val<vm_channel_t>(reinterpret_cast<pointer_t>(&channel_stack_layout.alloc));
1130
1131 auto request = vm_channel_request_t{ current_thread_id, r.request_mutex };
1132 request.data = reinterpret_cast<pointer_t>(channel_stack_layout.data);
1133 request.request_handled_callback = alt_request_handled_callback;
1134
1135 if (rnd_idx < alt->send_count)
1136 handled = channel->send_data(request);
1137 else
1138 handled = channel->receive_data(request);
1139
1140 if (handled)
1141 {
1142 // Check if requests need to be handled
1143 if (requests_queued > 0)
1144 {
1145 auto handled_index = cancel_outstanding_channel_requests(current_thread_id, alt, *channel);
1146 assert(handled_index == rnd_idx);
1147 }
1148
1149 vt_ref<word_t>(r.dest) = rnd_idx;
1150 r.current_thread_state = vm_thread_state_t::running;
1151 return;
1152 }
1153 else
1154 {
1155 requests_queued++;
1156 }
1157 }
1158
1159 // If this is a non-blocking alt request and we are here no channels
1160 // were immediately able to handle the request. Try to lock the request
1161 // so we can cancel it in all channels.
1162 if (!blocking && r.request_mutex.pending_request_lock.try_lock())
1163 {
1164 std::lock_guard<std::mutex> lock{ r.request_mutex.pending_request_lock, std::adopt_lock };
1165 // Check if a channel handled the request between the initial queueing and now.
1166 if (r.request_mutex.pending_request)
1167 {
1168 r.request_mutex.pending_request = false;
1169
1170 if (debug::is_component_tracing_enabled<debug::component_trace_t::channel>())
1171 debug::log_msg(debug::component_trace_t::channel, debug::log_level_t::debug, "alt: request: non-blocking\n");
1172
1173 // Cancel request in all channels.
1174 for (auto i = word_t{ 0 }; i < channel_count; ++i)
1175 {
1176 auto ch_pointer = reinterpret_cast<pointer_t>(alt->channels[i].alloc);
1177 auto ch = at_val<vm_channel_t>(reinterpret_cast<pointer_t>(&ch_pointer));
1178 ch->cancel_request(current_thread_id);
1179 }
1180
1181 // Index of a non-blocking request is the final index of the alt.
1182 vt_ref<word_t>(r.dest) = channel_count;
1183 r.current_thread_state = vm_thread_state_t::running;
1184 }
1185 }
1186 }
1187 }
1188
1189 EXEC_DECL(alt)
1190 {
1191 exec_alt(r, vm, true /* blocking */);
1192 }
1193
1194 EXEC_DECL(nbalt)
1195 {
1196 exec_alt(r, vm, false /* blocking */);
1197 }
1198
1199 //
1200 // Dynamic allocation operations
1201 //
1202
1203 namespace
1204 {
1205 void new_instance(vm_registers_t &r, const vm_t &vm, const bool zero_memory)
1206 {
1207 const auto type_id = vt_ref<word_t>(r.src);
1208 const auto &types = r.module_ref->type_section;
1209 assert(0 <= type_id && type_id < static_cast<word_t>(types.size()));
1210
1211 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
1212
1213 auto type_desc = types[type_id];
1214 auto new_alloc = !zero_memory ? vm_alloc_t::allocate(type_desc) : vm_alloc_t::allocate(type_desc, vm_alloc_t::zero_memory);
1215
1216 if (type_desc->map_in_bytes > 0)
1217 vm.get_garbage_collector().track_allocation(new_alloc);
1218
1219 pt_ref(r.dest) = new_alloc->allocation();
1220 }
1221 }
1222
1223 EXEC_DECL(new_)
1224 {
1225 new_instance(r, vm, false /* zero memory */);
1226 }
1227
1228 EXEC_DECL(newz)
1229 {
1230 new_instance(r, vm, true /* zero memory */);
1231 }
1232
1233 EXEC_DECL(mnewz)
1234 {
1235 auto target_module = at_val<vm_module_ref_t>(r.src);
1236 if (target_module == nullptr)
1237 throw vm_user_exception{ "Module not loaded" };
1238
1239 const auto type_id = vt_ref<word_t>(r.mid);
1240 const auto &types = target_module->type_section;
1241 assert(0 <= type_id && type_id < static_cast<word_t>(types.size()));
1242
1243 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
1244
1245 auto type_desc = types[type_id];
1246 auto new_alloc = vm_alloc_t::allocate(type_desc, vm_alloc_t::zero_memory);
1247
1248 if (type_desc->map_in_bytes > 0)
1249 vm.get_garbage_collector().track_allocation(new_alloc);
1250
1251 pt_ref(r.dest) = new_alloc->allocation();
1252 }
1253
1254 namespace
1255 {
1256 void new_array(vm_registers_t &r, const vm_t &vm, const bool zero_memory)
1257 {
1258 assert(r.module_ref != nullptr);
1259 const auto &types = r.module_ref->type_section;
1260
1261 const auto type_id = vt_ref<word_t>(r.mid);
1262 assert(0 <= type_id && type_id < static_cast<word_t>(types.size()));
1263 auto type_desc = types[type_id];
1264
1265 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
1266
1267 const auto array_size = vt_ref<word_t>(r.src);
1268 auto new_array = !zero_memory ? new vm_array_t(type_desc, array_size) : new vm_array_t(type_desc, array_size, vm_alloc_t::zero_memory);
1269
1270 if (type_desc->map_in_bytes > 0)
1271 vm.get_garbage_collector().track_allocation(new_array);
1272
1273 pt_ref(r.dest) = new_array->allocation();
1274 }
1275 }
1276
1277 EXEC_DECL(newa)
1278 {
1279 new_array(r, vm, false /* zero memory */);
1280 }
1281
1282 EXEC_DECL(newaz)
1283 {
1284 new_array(r, vm, true /* zero memory */);
1285 }
1286
1287 EXEC_DECL(tcmp)
1288 {
1289 auto s = at_val<vm_alloc_t>(r.src);
1290 auto d = at_val<vm_alloc_t>(r.dest);
1291
1292 if (s == nullptr)
1293 return;
1294
1295 if (d == nullptr || s->alloc_type != d->alloc_type)
1296 throw type_violation{};
1297 }
1298
1299 //
1300 // Stack operations
1301 //
1302
1303 // [SPEC] 'runt' op code
1304 // Undocumented: (http://www.vitanuova.com/inferno/papers/dis.html)
1305 //
1306 // Inferno implementation: (libinterp/xec.c)
1307 // No operands and appears to be a type of NOP instruction.
1308 EXEC_DECL(runt) { /* NOP */ }
1309
1310 EXEC_DECL(lea) { pt_ref(r.dest) = r.src; }
1311
1312 EXEC_DECL(frame)
1313 {
1314 const auto &types = r.module_ref->type_section;
1315
1316 const auto frame_type_id = vt_ref<word_t>(r.src);
1317 assert(0 <= frame_type_id && frame_type_id < static_cast<word_t>(types.size()));
1318 const auto frame_type = types[frame_type_id];
1319
1320 pt_ref(r.dest) = r.stack.alloc_frame(frame_type)->base();
1321 }
1322
1323 EXEC_DECL(mframe)
1324 {
1325 const auto target_module = at_val<vm_module_ref_t>(r.src);
1326 if (target_module == nullptr)
1327 throw vm_user_exception{ "Module not loaded" };
1328
1329 const auto &types = target_module->type_section;
1330
1331 // Get the function ref index for this module.
1332 const auto function_id = vt_ref<word_t>(r.mid);
1333 const auto &function_ref = target_module->get_function_ref(function_id);
1334
1335 const auto frame_type_id = function_ref.frame_type;
1336 assert(0 <= frame_type_id && frame_type_id < static_cast<word_t>(types.size()));
1337 const auto frame_type = types[frame_type_id];
1338
1339 pt_ref(r.dest) = r.stack.alloc_frame(frame_type)->base();
1340 }
1341
1342 EXEC_DECL(ret)
1343 {
1344 const auto current_top_frame = r.stack.peek_frame();
1345 assert(current_top_frame != nullptr);
1346 r.pc = current_top_frame->prev_pc();
1347
1348 if (current_top_frame->prev_module_ref() != nullptr)
1349 {
1350 dec_ref_count_and_free(r.module_ref);
1351 r.module_ref = current_top_frame->prev_module_ref();
1352 current_top_frame->prev_module_ref() = nullptr;
1353
1354 dec_ref_count_and_free(r.mp_base);
1355 r.mp_base = r.module_ref->mp_base;
1356 }
1357
1358 r.stack.pop_frame();
1359
1360 if (debug::is_component_tracing_enabled<debug::component_trace_t::stack>())
1361 debug::log_msg(debug::component_trace_t::stack, debug::log_level_t::debug, "exit: function\n");
1362 }
1363
1364 EXEC_DECL(call)
1365 {
1366 auto top_frame = r.stack.push_frame();
1367 top_frame->prev_pc() = r.pc;
1368
1369#ifndef NDEBUG
1370 // Validate the stack state
1371 const auto frame_base_pointer = pt_ref(r.src);
1372 assert(frame_base_pointer != nullptr);
1373 assert(top_frame->base() == frame_base_pointer);
1374#endif
1375
1376 r.pc = vt_ref<vm_pc_t>(r.dest);
1377
1378 debug::log_msg(debug::component_trace_t::stack, debug::log_level_t::debug, "enter: function\n");
1379 }
1380
1381 EXEC_DECL(mcall)
1382 {
1383 auto target_module = at_val<vm_module_ref_t>(r.dest);
1384 if (target_module == nullptr)
1385 throw vm_user_exception{ "Module not loaded" };
1386
1387 // Get the pc for the function call into this module
1388 const auto export_id = vt_ref<word_t>(r.mid);
1389 const auto &function_ref = target_module->get_function_ref(export_id);
1390 const auto function_pc = function_ref.entry_pc;
1391 assert(static_cast<std::size_t>(function_pc) < target_module->code_section.size());
1392
1393 // Push the next frame
1394 auto top_frame = r.stack.push_frame();
1395
1396#ifndef NDEBUG
1397 // Validate the stack state
1398 const auto frame_base_pointer = pt_ref(r.src);
1399 assert(frame_base_pointer != nullptr);
1400 assert(top_frame->base() == frame_base_pointer);
1401#endif
1402
1403 // Store current state
1404 top_frame->prev_pc() = r.pc;
1405 top_frame->prev_module_ref() = r.module_ref;
1406
1407 if (debug::is_component_tracing_enabled<debug::component_trace_t::stack>())
1408 {
1409 debug::log_msg(
1410 debug::component_trace_t::stack,
1411 debug::log_level_t::debug,
1412 "enter: function: inter-module: %d >>%s<< %d %s\n",
1413 export_id,
1414 target_module->module->module_name->str(),
1415 function_pc,
1416 (target_module->is_builtin_module() ? "true" : "false"));
1417 }
1418
1419 // Set registers for execution
1420 r.module_ref = target_module;
1421 r.module_ref->add_ref();
1422 r.mp_base = r.module_ref->mp_base;
1423 r.pc = function_pc;
1424
1425 if (r.module_ref->is_builtin_module())
1426 {
1427 // Execute the built-in function, then return
1428 const auto &inst = r.module_ref->code_section[function_pc];
1429 assert(r.mp_base == nullptr && "Built-in modules shouldn't have module data (MP register)");
1430
1431 r.current_thread_state = vm_thread_state_t::release;
1432 inst.native(r, vm);
1433 ret(r, vm);
1434 r.current_thread_state = vm_thread_state_t::running;
1435 }
1436 else
1437 {
1438 r.mp_base->add_ref();
1439 }
1440 }
1441
1442 EXEC_DECL(jmp) { r.pc = vt_ref<vm_pc_t>(r.dest); }
1443
1444 EXEC_DECL(goto_)
1445 {
1446 auto pc_index = vt_ref<word_t>(r.src);
1447
1448 assert(r.dest != nullptr);
1449 auto pc_table = reinterpret_cast<vm_pc_t *>(r.dest);
1450
1451 r.pc = pc_table[pc_index];
1452 }
1453
1454 //
1455 // Thread operations
1456 //
1457
1458 EXEC_DECL(spawn)
1459 {
1460 // Arguments to the spawned thread are passed on the current
1461 // thread's stack in a new frame.
1462 auto spawned_frame = r.stack.push_frame();
1463
1464#ifndef NDEBUG
1465 // Validate the stack state
1466 const auto frame_base_pointer = pt_ref(r.src);
1467 assert(frame_base_pointer != nullptr);
1468 assert(spawned_frame->base() == frame_base_pointer);
1469#endif
1470
1471 const auto starting_pc = vt_ref<vm_pc_t>(r.dest);
1472
1473 assert(r.module_ref != nullptr);
1474 vm.fork(r.thread, *r.module_ref, *spawned_frame, starting_pc);
1475
1476 // After the new thread is forked pop off the argument passing frame.
1477 r.stack.pop_frame();
1478 }
1479
1480 EXEC_DECL(mspawn)
1481 {
1482 // Arguments to the spawned thread are passed on the current
1483 // thread's stack in a new frame.
1484 auto spawned_frame = r.stack.push_frame();
1485
1486#ifndef NDEBUG
1487 // Validate the stack state
1488 const auto frame_base_pointer = pt_ref(r.src);
1489 assert(frame_base_pointer != nullptr);
1490 assert(spawned_frame->base() == frame_base_pointer);
1491#endif
1492
1493 const auto import_function_id = vt_ref<word_t>(r.mid);
1494 auto target_module_ref = at_val<vm_module_ref_t>(r.dest);
1495 assert(target_module_ref != nullptr);
1496
1497 const auto &function_ref = target_module_ref->get_function_ref(import_function_id);
1498
1499 vm.fork(r.thread, *target_module_ref, *spawned_frame, function_ref.entry_pc);
1500
1501 // After the new thread is forked pop off the argument passing frame.
1502 r.stack.pop_frame();
1503 }
1504
1505 EXEC_DECL(exit)
1506 {
1507 assert(false && "test failure");
1508 r.current_thread_state = vm_thread_state_t::exiting;
1509 }
1510
1511 //
1512 // Exception operations
1513 //
1514
1515 EXEC_DECL(raise)
1516 {
1517 auto e = at_val<vm_alloc_t>(r.src);
1518 if (e == nullptr)
1519 throw dereference_nil{ "Raise exception" };
1520
1521 const vm_string_t *exception_id;
1522
1523 // Determine if a 'string' or 'exception' type was raised.
1524 if (e->alloc_type == vm_string_t::type_desc())
1525 {
1526 // String
1527 exception_id = dynamic_cast<vm_string_t *>(e);
1528
1529 if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>())
1530 debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "raise: string: >>%s<<\n", exception_id->str());
1531 }
1532 else
1533 {
1534 // [SPEC] The exception format is not formally specified.
1535 // The first pointer in the exception is a string type
1536 // that is used to uniquely identify the exception type.
1537 // The remainder of the type is user-defined.
1538 auto content = e->allocation();
1539 exception_id = at_val<vm_string_t>(content);
1540
1541 if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>())
1542 debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "raise: exception: >>%s<<\n", exception_id->str());
1543 }
1544
1545 const handler_t *handler;
1546 vm_frame_t *target_frame;
1547 vm_pc_t handler_pc;
1548
1549 // The faulting pc is minus one since the VM has already incremented the counter.
1550 const auto faulting_pc = r.pc - 1;
1551
1552 assert(r.module_ref != nullptr);
1553 std::tie(handler, target_frame, handler_pc) = find_exception_handler(r.stack, *r.module_ref, faulting_pc, *exception_id);
1554 assert(target_frame != nullptr);
1555
1556 // Now that we know the exception has a handler, take a reference.
1557 e->add_ref();
1558
1559 if (target_frame != r.stack.peek_frame())
1560 {
1561 while (target_frame != r.stack.pop_frame())
1562 {
1563 // Remove frames from the stack until the target frame is found.
1564 }
1565 }
1566
1567 // Re-initialize the current frame
1568 if (handler->type_desc != nullptr)
1569 {
1570 destroy_memory(*handler->type_desc, target_frame->base());
1571 init_memory(*handler->type_desc, target_frame->base());
1572 }
1573
1574 // Set the exception at the appropriate frame offset
1575 auto frame_pointer = reinterpret_cast<uint8_t *>(target_frame->base());
1576 auto exception_destination = reinterpret_cast<pointer_t>(frame_pointer + handler->exception_offset);
1577
1578 // Destroy any data at this location.
1579 auto current_value = at_val<vm_alloc_t>(exception_destination);
1580 dec_ref_count_and_free(current_value);
1581
1582 // Set the exception destination.
1583 pt_ref(exception_destination) = e->allocation();
1584
1585 r.pc = handler_pc;
1586 }
1587
1588 //
1589 // Module operations
1590 //
1591
1592 EXEC_DECL(load)
1593 {
1594 // Validate module loading module.
1595 auto importing_module = r.module_ref->module;
1596 if (!util::has_flag(importing_module->header.runtime_flag, runtime_flags_t::has_import))
1597 throw vm_user_exception{ "Invalid importing module" };
1598
1599 auto str = at_val<vm_string_t>(r.src);
1600 assert(str != nullptr);
1601
1602 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
1603 pt_ref(r.dest) = nullptr;
1604
1605 auto imported_module = std::shared_ptr<const vm_module_t>{};
1606
1607 // Handle self loading module optimization.
1608 if (::strcmp(str->str(), "$self") == 0)
1609 {
1610 imported_module = r.module_ref->module;
1611 }
1612 else
1613 {
1614 try
1615 {
1616 imported_module = vm.load_module(str->str());
1617 }
1618 catch (const vm_module_exception &e)
1619 {
1620 debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "exception: load: >>%s<<\n", e.what());
1621 assert(false && "Failure reading module");
1622 return;
1623 }
1624 }
1625
1626 auto module_import_index = vt_ref<uint32_t>(r.mid);
1627 const auto &imports = importing_module->import_section;
1628
1629 assert(module_import_index < imports.size());
1630 const auto &function_imports = imports[module_import_index];
1631
1632 auto entry_module_ref = new vm_module_ref_t{ imported_module, function_imports };
1633 pt_ref(r.dest) = entry_module_ref->allocation();
1634 }
1635}
1636
1637const vm_exec_t disvm::runtime::vm_exec_table[static_cast<std::size_t>(opcode_t::last_opcode) + 1] =
1638{
1639 invalid,
1640 alt,
1641 nbalt,
1642 goto_,
1643 call,
1644 frame,
1645 spawn,
1646 runt,
1647 load,
1648 mcall,
1649 mspawn,
1650 mframe,
1651 ret,
1652 jmp,
1653 casew,
1654 exit,
1655 new_,
1656 newa,
1657 newcb,
1658 newcw,
1659 newcf,
1660 newcp,
1661 newcm,
1662 newcmp,
1663 send,
1664 recv,
1665 consb,
1666 consw,
1667 consp,
1668 consf,
1669 notimpl, // consm,
1670 consmp,
1671 headb,
1672 headw,
1673 headp,
1674 headf,
1675 notimpl, // headm,
1676 headmp,
1677 tail,
1678 lea,
1679 indx,
1680 movp,
1681 movm,
1682 movmp,
1683 movb,
1684 movw,
1685 movf,
1686 cvtbw,
1687 cvtwb,
1688 cvtfw,
1689 cvtwf,
1690 cvtca,
1691 cvtac,
1692 cvtwc,
1693 cvtcw,
1694 cvtfc,
1695 cvtcf,
1696 addb,
1697 addw,
1698 addf,
1699 subb,
1700 subw,
1701 subf,
1702 mulb,
1703 mulw,
1704 mulf,
1705 divb,
1706 divw,
1707 divf,
1708 modw,
1709 modb,
1710 andb,
1711 andw,
1712 orb,
1713 orw,
1714 xorb,
1715 xorw,
1716 shlb,
1717 shlw,
1718 shrb,
1719 shrw,
1720 insc,
1721 indc,
1722 addc,
1723 lenc,
1724 lena,
1725 lenl,
1726 beqb,
1727 bneb,
1728 bltb,
1729 bleb,
1730 bgtb,
1731 bgeb,
1732 beqw,
1733 bnew,
1734 bltw,
1735 blew,
1736 bgtw,
1737 bgew,
1738 beqf,
1739 bnef,
1740 bltf,
1741 blef,
1742 bgtf,
1743 bgef,
1744 beqc,
1745 bnec,
1746 bltc,
1747 blec,
1748 bgtc,
1749 bgec,
1750 slicea,
1751 slicela,
1752 slicec,
1753 indw,
1754 indf,
1755 indb,
1756 negf,
1757 movl,
1758 addl,
1759 subl,
1760 divl,
1761 modl,
1762 mull,
1763 andl,
1764 orl,
1765 xorl,
1766 shll,
1767 shrl,
1768 bnel,
1769 bltl,
1770 blel,
1771 bgtl,
1772 bgel,
1773 beql,
1774 cvtlf,
1775 cvtfl,
1776 cvtlw,
1777 cvtwl,
1778 cvtlc,
1779 cvtcl,
1780 headl,
1781 consl,
1782 newcl,
1783 casec,
1784 indl,
1785 notimpl, // movpc,
1786 tcmp,
1787 mnewz,
1788 cvtrf,
1789 cvtfr,
1790 cvtws,
1791 cvtsw,
1792 lsrw,
1793 lsrl,
1794 eclr,
1795 newz,
1796 newaz,
1797 raise,
1798 casel,
1799 mulx,
1800 divx,
1801 cvtxx,
1802 notimpl, // mulx0,
1803 notimpl, // divx0,
1804 notimpl, // cvtxx0,
1805 notimpl, // mulx1,
1806 notimpl, // divx1,
1807 notimpl, // cvtxx1,
1808 cvtfx,
1809 cvtxf,
1810 expw,
1811 expl,
1812 expf,
1813 notimpl, // self,
1814};