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+
22+using namespace disvm;
23+using namespace disvm::runtime;
24+
25+namespace
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+
1637+const 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+};