//// Dis VM// File: execution_table.cpp// Author: arr// #include <type_traits>#include <numeric>#include <algorithm>#include <array>#include <cstdint>#include <limits>#include <random>#include <vector>#include <disvm.h>#include <vm_memory.h>#include <opcodes.h>#include <utils.h>#include <exceptions.h>#include <debug.h>#include <builtin_module.h>#include "tool_dispatch.h"#include "execution_table.h" using namespace disvm;using namespace disvm::runtime; namespace{ // // Private VM primitive data type definitions // using uword_t = uint32_t; // unsigned word_t using ubig_t = uint64_t; // unsigned big_t #define EXEC_DECL(inst_name) void inst_name(vm_registers_t &r, vm_t& vm) EXEC_DECL(notimpl) { assert(false && "Instruction not implemented"); throw vm_system_exception{ "Instruction not implemented" }; } EXEC_DECL(invalid) { throw vm_system_exception{ "Illegal Dis VM instruction" }; } // [SPEC] Documented as 'Clear exception stack' and defined to be used at the discretion of the VM implementation. // Implemented as a 'spare slot' in Inferno EXEC_DECL(eclr) { assert(false && "The 'eclr' instruction is not expected to be used"); } // // Bit-wise operations // EXEC_DECL(andb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) & vt_ref<byte_t>(r.mid); } EXEC_DECL(andw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) & vt_ref<word_t>(r.mid); } EXEC_DECL(andl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) & vt_ref<big_t>(r.mid); } EXEC_DECL(orb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) | vt_ref<byte_t>(r.mid); } EXEC_DECL(orw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) | vt_ref<word_t>(r.mid); } EXEC_DECL(orl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) | vt_ref<big_t>(r.mid); } EXEC_DECL(xorb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) ^ vt_ref<byte_t>(r.mid); } EXEC_DECL(xorw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) ^ vt_ref<word_t>(r.mid); } EXEC_DECL(xorl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) ^ vt_ref<big_t>(r.mid); } // // Bit-shift operations // EXEC_DECL(shlb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.mid) << vt_ref<byte_t>(r.src); } EXEC_DECL(shlw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.mid) << vt_ref<word_t>(r.src); } EXEC_DECL(shll) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) << vt_ref<big_t>(r.src); } EXEC_DECL(shrb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.mid) >> vt_ref<byte_t>(r.src); } EXEC_DECL(shrw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.mid) >> vt_ref<word_t>(r.src); // Right shifting a negative number is implementation dependent in C++. // Assert the current compiler defaults to arithmetic shifting, not logical. assert(vt_ref<word_t>(r.mid) >= 0 || (vt_ref<word_t>(r.mid) < 0 && vt_ref<word_t>(r.dest) < 0)); } EXEC_DECL(shrl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) >> vt_ref<big_t>(r.src); // Right shifting a negative number is implementation dependent in C++. // Assert the current compiler defaults to arithmetic shifting, not logical. assert(vt_ref<big_t>(r.mid) >= 0 || (vt_ref<big_t>(r.mid) < 0 && vt_ref<big_t>(r.dest) < 0)); } EXEC_DECL(lsrw) { vt_ref<word_t>(r.dest) = vt_ref<uword_t>(r.mid) >> vt_ref<word_t>(r.src); } EXEC_DECL(lsrl) { vt_ref<big_t>(r.dest) = vt_ref<ubig_t>(r.mid) >> vt_ref<big_t>(r.src); } // // Conversion operations // EXEC_DECL(cvtbw) { vt_ref<word_t>(r.dest) = vt_ref<byte_t>(r.src); } EXEC_DECL(cvtwb) { vt_ref<byte_t>(r.dest) = vt_ref<word_t>(r.src); } EXEC_DECL(cvtwl) { vt_ref<big_t>(r.dest) = vt_ref<word_t>(r.src); } EXEC_DECL(cvtlw) { vt_ref<word_t>(r.dest) = static_cast<word_t>(vt_ref<big_t>(r.src)); } EXEC_DECL(cvtrf) { vt_ref<short_real_t>(r.dest) = static_cast<short_real_t>(vt_ref<real_t>(r.src)); } EXEC_DECL(cvtfr) { vt_ref<real_t>(r.dest) = static_cast<real_t>(vt_ref<short_real_t>(r.src)); } EXEC_DECL(cvtws) { vt_ref<short_word_t>(r.dest) = vt_ref<word_t>(r.src); } EXEC_DECL(cvtsw) { vt_ref<word_t>(r.dest) = vt_ref<short_word_t>(r.src); } EXEC_DECL(cvtlf) { vt_ref<real_t>(r.dest) = static_cast<real_t>(vt_ref<big_t>(r.src)); } EXEC_DECL(cvtfl) { auto f = vt_ref<real_t>(r.src); f = f < 0 ? (f - 0.5) : (f + 0.5); vt_ref<big_t>(r.dest) = static_cast<big_t>(f); } 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); } EXEC_DECL(cvtfx) { auto f = vt_ref<real_t>(r.src) * vt_ref<real_t>(r.mid); f = f < 0 ? (f - 0.5) : (f + 0.5); vt_ref<word_t>(r.dest) = static_cast<word_t>(f); } EXEC_DECL(cvtwf) { vt_ref<real_t>(r.dest) = static_cast<real_t>(vt_ref<word_t>(r.src)); } EXEC_DECL(cvtfw) { auto f = vt_ref<real_t>(r.src); f = f < 0 ? (f - 0.5) : (f + 0.5); vt_ref<word_t>(r.dest) = static_cast<word_t>(f); } // [SPEC] Op code for converting 'Fixed point' value to word_t // Undocumented: (http://www.vitanuova.com/inferno/papers/dis.html) EXEC_DECL(cvtxx) { const auto pow2_scale = vt_ref<word_t>(r.mid); auto res = big_t{ vt_ref<word_t>(r.src) }; if (pow2_scale >= 0) res <<= pow2_scale; else res >>= (-pow2_scale); vt_ref<word_t>(r.dest) = static_cast<word_t>(res); } // [SPEC] Op code for converting 'Fixed point' value to word_t // Undocumented: (http://www.vitanuova.com/inferno/papers/dis.html) EXEC_DECL(cvtxx0) { auto tmp = big_t{ vt_ref<word_t>(r.src) }; if (tmp == 0) { vt_ref<word_t>(r.dest) = 0; return; } const auto pow2_scale = vt_ref<word_t>(r.mid); if (pow2_scale >= 0) tmp <<= pow2_scale; else tmp >>= (-pow2_scale); const auto residual_scale = *reinterpret_cast<word_t *>(r.stack.peek_frame()->fixed_point_register_1()); assert(residual_scale != 0); const auto result = tmp / residual_scale; vt_ref<word_t>(r.dest) = static_cast<word_t>(result); } EXEC_DECL(cvtca) { dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); auto str = at_val<vm_string_t>(r.src); auto new_array = new vm_array_t{ str }; pt_ref(r.dest) = new_array->get_allocation(); } EXEC_DECL(cvtac) { dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); auto str = pointer_t{}; auto arr = at_val<vm_array_t>(r.src); if (arr != nullptr) { if (arr->get_element_type() != intrinsic_type_desc::type<byte_t>()) throw vm_user_exception{ "Invalid array element type for string conversion" }; auto new_string = new vm_string_t{ static_cast<std::size_t>(arr->get_length()), reinterpret_cast<uint8_t *>(arr->at(0)) }; str = new_string->get_allocation(); } pt_ref(r.dest) = str; } EXEC_DECL(cvtcw) { auto result = word_t{ 0 }; auto str = at_val<vm_string_t>(r.src); if (str != nullptr) result = static_cast<word_t>(::strtol(str->str(), nullptr, 10)); vt_ref<word_t>(r.dest) = result; } EXEC_DECL(cvtwc) { // String represents the largest possible string from a word. auto buffer = std::array<char, sizeof("-2147483648")>{}; const auto w = vt_ref<word_t>(r.src); const auto len = static_cast<std::size_t>(std::sprintf(buffer.data(), "%d", w)); assert(len <= buffer.size()); auto new_string = new vm_string_t{ len, reinterpret_cast<uint8_t *>(buffer.data()) }; dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); pt_ref(r.dest) = new_string->get_allocation(); } EXEC_DECL(cvtcf) { auto result = real_t{ 0.0 }; auto str = at_val<vm_string_t>(r.src); if (str != nullptr) result = static_cast<real_t>(::strtod(str->str(), nullptr)); vt_ref<real_t>(r.dest) = result; } EXEC_DECL(cvtfc) { // String represents the largest possible string from a double with scientific notation. auto buffer = std::array<char, sizeof("-2.2250738585072014e-308")>{}; const auto real = vt_ref<real_t>(r.src); const auto len = static_cast<std::size_t>(std::sprintf(buffer.data(), "%g", real)); assert(len <= buffer.size()); auto new_string = new vm_string_t{ len, reinterpret_cast<uint8_t *>(buffer.data()) }; dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); pt_ref(r.dest) = new_string->get_allocation(); } EXEC_DECL(cvtcl) { auto result = big_t{ 0 }; auto str = at_val<vm_string_t>(r.src); if (str != nullptr) result = static_cast<big_t>(::strtoll(str->str(), nullptr, 10)); vt_ref<big_t>(r.dest) = result; } EXEC_DECL(cvtlc) { // String represents the largest possible string from a 64-bit integer. auto buffer = std::array<char, sizeof("-9223372036854775808")>{}; const auto b = vt_ref<big_t>(r.src); const auto len = static_cast<std::size_t>(std::sprintf(buffer.data(), "%lld", b)); assert(len <= buffer.size()); auto new_string = new vm_string_t{ len, reinterpret_cast<uint8_t *>(buffer.data()) }; dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); pt_ref(r.dest) = new_string->get_allocation(); } // // Arithmetic operations // EXEC_DECL(negf) { vt_ref<real_t>(r.dest) = -vt_ref<real_t>(r.src); } EXEC_DECL(addb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) + vt_ref<byte_t>(r.mid); } EXEC_DECL(addw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) + vt_ref<word_t>(r.mid); } EXEC_DECL(addl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) + vt_ref<big_t>(r.mid); } EXEC_DECL(addf) { vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.src) + vt_ref<real_t>(r.mid); } EXEC_DECL(subb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.mid) - vt_ref<byte_t>(r.src); } EXEC_DECL(subw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.mid) - vt_ref<word_t>(r.src); } EXEC_DECL(subl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) - vt_ref<big_t>(r.src); } EXEC_DECL(subf) { vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.mid) - vt_ref<real_t>(r.src); } EXEC_DECL(mulb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) * vt_ref<byte_t>(r.mid); } EXEC_DECL(mulw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) * vt_ref<word_t>(r.mid); } EXEC_DECL(mull) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) * vt_ref<big_t>(r.mid); } EXEC_DECL(mulf) { vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.src) * vt_ref<real_t>(r.mid); } template<typename PrimitiveType> void _div(vm_registers_t &r) { const auto denom = vt_ref<PrimitiveType>(r.src); if (denom == PrimitiveType{ 0 }) throw divide_by_zero{}; vt_ref<PrimitiveType>(r.dest) = vt_ref<PrimitiveType>(r.mid) / denom; } // [SPEC] The C++ defines division by 0 as undefined, however the IEEE standard does // define the behavior. Therefore the behavior is platform dependent and controlled // by the built-in Math module. template<> void _div<real_t>(vm_registers_t &r) { const auto denom = vt_ref<real_t>(r.src); vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.mid) / denom; } EXEC_DECL(divb) { _div<byte_t>(r); } EXEC_DECL(divw) { _div<word_t>(r); } EXEC_DECL(divf) { _div<real_t>(r); } EXEC_DECL(divl) { _div<big_t>(r); } template<typename PrimitiveType> void _mod(vm_registers_t &r) { const auto denom = vt_ref<PrimitiveType>(r.src); if (denom == PrimitiveType{ 0 }) throw divide_by_zero{}; vt_ref<PrimitiveType>(r.dest) = vt_ref<PrimitiveType>(r.mid) % denom; } EXEC_DECL(modb) { _mod<byte_t>(r); } EXEC_DECL(modw) { _mod<word_t>(r); } EXEC_DECL(modl) { _mod<big_t>(r); } // [SPEC] 'Exponent' op code // Undocumented: (http://www.vitanuova.com/inferno/papers/dis.html) // Limbo reference: (http://www.vitanuova.com/inferno/papers/addendum.pdf) // // Inferno implementation: (libinterp/xec.c) // src1 = power // src2 = base // // exp[w|l|f] src1 src2 dst // dst = src2 'raised to' src1 // // Result type matches the type of base. Power should be type 'word'. template<typename PrimitiveType> void _exp(vm_registers_t &r) { auto base = vt_ref<PrimitiveType>(r.mid); auto power = vt_ref<word_t>(r.src); auto inverse = false; if (power < 0) { power = (-power); inverse = true; } auto result = PrimitiveType{ 1 }; for (;;) { if ((power & 1) != 0) result *= base; power >>= 1; if (power == 0) break; base *= base; } if (inverse) result = (1 / result); vt_ref<PrimitiveType>(r.dest) = result; } EXEC_DECL(expw) { _exp<word_t>(r); } EXEC_DECL(expl) { _exp<big_t>(r); } EXEC_DECL(expf) { _exp<real_t>(r); } // [SPEC] 'Fixed point' op codes // Undocumented: (http://www.vitanuova.com/inferno/papers/dis.html) // Limbo reference: (http://www.vitanuova.com/inferno/papers/addendum.pdf) // Fixed point identities // X = s * x // Y = t * y // Z = u * z // // Multiplication: // uz = sx * ty // z = (sx/u) * (ty/u) // z = (st/u) * (xy) // // Division: // uz = sx/ty // z = (sx/ty) * (1/u) // z = (s/tu) * (x/y) // Multiply for fixed point types with final scaling as a power of 2. // mulx src1 src2 dst // src1 - integer 1 (y) // src2 - integer 2 (x) // fpr_2 - result scaling factor (2^p) // dest - result integer (z) EXEC_DECL(mulx) { const auto x = big_t{ vt_ref<word_t>(r.mid) }; const auto y = big_t{ vt_ref<word_t>(r.src) }; auto z = x * y; const auto pow2_scale = *reinterpret_cast<word_t *>(r.stack.peek_frame()->fixed_point_register_2()); if (pow2_scale >= 0) z <<= pow2_scale; else z >>= (-pow2_scale); vt_ref<word_t>(r.dest) = static_cast<word_t>(z); } // Division for fixed point types with final scaling as a power of 2. // divx src1 src2 dst // src1 - integer 1 (y) // src2 - integer 2 (x) // fpr_2 - result scaling factor (2^p) // dest - result integer (z) EXEC_DECL(divx) { auto x = big_t{ vt_ref<word_t>(r.mid) }; const auto pow2_scale = *reinterpret_cast<word_t *>(r.stack.peek_frame()->fixed_point_register_2()); if (pow2_scale >= 0) x <<= pow2_scale; else x >>= (-pow2_scale); const auto y = big_t{ vt_ref<word_t>(r.src) }; if (y == 0) throw divide_by_zero{}; const auto z = x / y; vt_ref<word_t>(r.dest) = static_cast<word_t>(z); } // mulx0 src1 src2 dst // src1 - integer 1 (y) // src2 - integer 2 (x) // fpr_1 - residual factor // fpr_2 - result scaling factor (2^p) // dest - result integer (z) EXEC_DECL(mulx0) { const auto x = big_t{ vt_ref<word_t>(r.mid) }; const auto y = big_t{ vt_ref<word_t>(r.src) }; if (x == 0 || y == 0) { vt_ref<word_t>(r.dest) = 0; return; } auto tmp = x * y; const auto frame = r.stack.peek_frame(); const auto pow2_scale = *reinterpret_cast<word_t *>(frame->fixed_point_register_2()); if (pow2_scale >= 0) tmp <<= pow2_scale; else tmp >>= (-pow2_scale); const auto residual_scale = big_t{ *reinterpret_cast<word_t *>(frame->fixed_point_register_1()) }; assert(residual_scale != 0); const auto z = tmp / residual_scale; vt_ref<word_t>(r.dest) = static_cast<word_t>(z); } // divx0 src1 src2 dst // src1 - integer 1 (y) // src2 - integer 2 (x) // fpr_1 - residual factor // fpr_2 - result scaling factor (2^p) // dest - result integer (z) EXEC_DECL(divx0) { const auto x = big_t{ vt_ref<word_t>(r.mid) }; const auto y = big_t{ vt_ref<word_t>(r.src) }; if (y == 0) throw divide_by_zero{}; if (x == 0) { vt_ref<word_t>(r.dest) = 0; return; } const auto frame = r.stack.peek_frame(); const auto residual_scale = big_t{ *reinterpret_cast<word_t *>(frame->fixed_point_register_1()) }; auto tmp = x * residual_scale; const auto pow2_scale = *reinterpret_cast<word_t *>(frame->fixed_point_register_2()); if (pow2_scale >= 0) tmp <<= pow2_scale; else tmp >>= (-pow2_scale); const auto z = tmp / y; vt_ref<word_t>(r.dest) = static_cast<word_t>(z); } // // Move operations // Lines 1–500 of 2035. Load more
// // Dis VM // File: execution_table.cpp // Author: arr // #include <type_traits> #include <numeric> #include <algorithm> #include <array> #include <cstdint> #include <limits> #include <random> #include <vector> #include <disvm.h> #include <vm_memory.h> #include <opcodes.h> #include <utils.h> #include <exceptions.h> #include <debug.h> #include <builtin_module.h> #include "tool_dispatch.h" #include "execution_table.h" using namespace disvm; using namespace disvm::runtime; namespace { // // Private VM primitive data type definitions // using uword_t = uint32_t; // unsigned word_t using ubig_t = uint64_t; // unsigned big_t #define EXEC_DECL(inst_name) void inst_name(vm_registers_t &r, vm_t& vm) EXEC_DECL(notimpl) { assert(false && "Instruction not implemented"); throw vm_system_exception{ "Instruction not implemented" }; } EXEC_DECL(invalid) { throw vm_system_exception{ "Illegal Dis VM instruction" }; } // [SPEC] Documented as 'Clear exception stack' and defined to be used at the discretion of the VM implementation. // Implemented as a 'spare slot' in Inferno EXEC_DECL(eclr) { assert(false && "The 'eclr' instruction is not expected to be used"); } // // Bit-wise operations // EXEC_DECL(andb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) & vt_ref<byte_t>(r.mid); } EXEC_DECL(andw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) & vt_ref<word_t>(r.mid); } EXEC_DECL(andl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) & vt_ref<big_t>(r.mid); } EXEC_DECL(orb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) | vt_ref<byte_t>(r.mid); } EXEC_DECL(orw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) | vt_ref<word_t>(r.mid); } EXEC_DECL(orl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) | vt_ref<big_t>(r.mid); } EXEC_DECL(xorb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) ^ vt_ref<byte_t>(r.mid); } EXEC_DECL(xorw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) ^ vt_ref<word_t>(r.mid); } EXEC_DECL(xorl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) ^ vt_ref<big_t>(r.mid); } // // Bit-shift operations // EXEC_DECL(shlb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.mid) << vt_ref<byte_t>(r.src); } EXEC_DECL(shlw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.mid) << vt_ref<word_t>(r.src); } EXEC_DECL(shll) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) << vt_ref<big_t>(r.src); } EXEC_DECL(shrb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.mid) >> vt_ref<byte_t>(r.src); } EXEC_DECL(shrw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.mid) >> vt_ref<word_t>(r.src); // Right shifting a negative number is implementation dependent in C++. // Assert the current compiler defaults to arithmetic shifting, not logical. assert(vt_ref<word_t>(r.mid) >= 0 || (vt_ref<word_t>(r.mid) < 0 && vt_ref<word_t>(r.dest) < 0)); } EXEC_DECL(shrl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) >> vt_ref<big_t>(r.src); // Right shifting a negative number is implementation dependent in C++. // Assert the current compiler defaults to arithmetic shifting, not logical. assert(vt_ref<big_t>(r.mid) >= 0 || (vt_ref<big_t>(r.mid) < 0 && vt_ref<big_t>(r.dest) < 0)); } EXEC_DECL(lsrw) { vt_ref<word_t>(r.dest) = vt_ref<uword_t>(r.mid) >> vt_ref<word_t>(r.src); } EXEC_DECL(lsrl) { vt_ref<big_t>(r.dest) = vt_ref<ubig_t>(r.mid) >> vt_ref<big_t>(r.src); } // // Conversion operations // EXEC_DECL(cvtbw) { vt_ref<word_t>(r.dest) = vt_ref<byte_t>(r.src); } EXEC_DECL(cvtwb) { vt_ref<byte_t>(r.dest) = vt_ref<word_t>(r.src); } EXEC_DECL(cvtwl) { vt_ref<big_t>(r.dest) = vt_ref<word_t>(r.src); } EXEC_DECL(cvtlw) { vt_ref<word_t>(r.dest) = static_cast<word_t>(vt_ref<big_t>(r.src)); } EXEC_DECL(cvtrf) { vt_ref<short_real_t>(r.dest) = static_cast<short_real_t>(vt_ref<real_t>(r.src)); } EXEC_DECL(cvtfr) { vt_ref<real_t>(r.dest) = static_cast<real_t>(vt_ref<short_real_t>(r.src)); } EXEC_DECL(cvtws) { vt_ref<short_word_t>(r.dest) = vt_ref<word_t>(r.src); } EXEC_DECL(cvtsw) { vt_ref<word_t>(r.dest) = vt_ref<short_word_t>(r.src); } EXEC_DECL(cvtlf) { vt_ref<real_t>(r.dest) = static_cast<real_t>(vt_ref<big_t>(r.src)); } EXEC_DECL(cvtfl) { auto f = vt_ref<real_t>(r.src); f = f < 0 ? (f - 0.5) : (f + 0.5); vt_ref<big_t>(r.dest) = static_cast<big_t>(f); } 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); } EXEC_DECL(cvtfx) { auto f = vt_ref<real_t>(r.src) * vt_ref<real_t>(r.mid); f = f < 0 ? (f - 0.5) : (f + 0.5); vt_ref<word_t>(r.dest) = static_cast<word_t>(f); } EXEC_DECL(cvtwf) { vt_ref<real_t>(r.dest) = static_cast<real_t>(vt_ref<word_t>(r.src)); } EXEC_DECL(cvtfw) { auto f = vt_ref<real_t>(r.src); f = f < 0 ? (f - 0.5) : (f + 0.5); vt_ref<word_t>(r.dest) = static_cast<word_t>(f); } // [SPEC] Op code for converting 'Fixed point' value to word_t // Undocumented: (http://www.vitanuova.com/inferno/papers/dis.html) EXEC_DECL(cvtxx) { const auto pow2_scale = vt_ref<word_t>(r.mid); auto res = big_t{ vt_ref<word_t>(r.src) }; if (pow2_scale >= 0) res <<= pow2_scale; else res >>= (-pow2_scale); vt_ref<word_t>(r.dest) = static_cast<word_t>(res); } // [SPEC] Op code for converting 'Fixed point' value to word_t // Undocumented: (http://www.vitanuova.com/inferno/papers/dis.html) EXEC_DECL(cvtxx0) { auto tmp = big_t{ vt_ref<word_t>(r.src) }; if (tmp == 0) { vt_ref<word_t>(r.dest) = 0; return; } const auto pow2_scale = vt_ref<word_t>(r.mid); if (pow2_scale >= 0) tmp <<= pow2_scale; else tmp >>= (-pow2_scale); const auto residual_scale = *reinterpret_cast<word_t *>(r.stack.peek_frame()->fixed_point_register_1()); assert(residual_scale != 0); const auto result = tmp / residual_scale; vt_ref<word_t>(r.dest) = static_cast<word_t>(result); } EXEC_DECL(cvtca) { dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); auto str = at_val<vm_string_t>(r.src); auto new_array = new vm_array_t{ str }; pt_ref(r.dest) = new_array->get_allocation(); } EXEC_DECL(cvtac) { dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); auto str = pointer_t{}; auto arr = at_val<vm_array_t>(r.src); if (arr != nullptr) { if (arr->get_element_type() != intrinsic_type_desc::type<byte_t>()) throw vm_user_exception{ "Invalid array element type for string conversion" }; auto new_string = new vm_string_t{ static_cast<std::size_t>(arr->get_length()), reinterpret_cast<uint8_t *>(arr->at(0)) }; str = new_string->get_allocation(); } pt_ref(r.dest) = str; } EXEC_DECL(cvtcw) { auto result = word_t{ 0 }; auto str = at_val<vm_string_t>(r.src); if (str != nullptr) result = static_cast<word_t>(::strtol(str->str(), nullptr, 10)); vt_ref<word_t>(r.dest) = result; } EXEC_DECL(cvtwc) { // String represents the largest possible string from a word. auto buffer = std::array<char, sizeof("-2147483648")>{}; const auto w = vt_ref<word_t>(r.src); const auto len = static_cast<std::size_t>(std::sprintf(buffer.data(), "%d", w)); assert(len <= buffer.size()); auto new_string = new vm_string_t{ len, reinterpret_cast<uint8_t *>(buffer.data()) }; dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); pt_ref(r.dest) = new_string->get_allocation(); } EXEC_DECL(cvtcf) { auto result = real_t{ 0.0 }; auto str = at_val<vm_string_t>(r.src); if (str != nullptr) result = static_cast<real_t>(::strtod(str->str(), nullptr)); vt_ref<real_t>(r.dest) = result; } EXEC_DECL(cvtfc) { // String represents the largest possible string from a double with scientific notation. auto buffer = std::array<char, sizeof("-2.2250738585072014e-308")>{}; const auto real = vt_ref<real_t>(r.src); const auto len = static_cast<std::size_t>(std::sprintf(buffer.data(), "%g", real)); assert(len <= buffer.size()); auto new_string = new vm_string_t{ len, reinterpret_cast<uint8_t *>(buffer.data()) }; dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); pt_ref(r.dest) = new_string->get_allocation(); } EXEC_DECL(cvtcl) { auto result = big_t{ 0 }; auto str = at_val<vm_string_t>(r.src); if (str != nullptr) result = static_cast<big_t>(::strtoll(str->str(), nullptr, 10)); vt_ref<big_t>(r.dest) = result; } EXEC_DECL(cvtlc) { // String represents the largest possible string from a 64-bit integer. auto buffer = std::array<char, sizeof("-9223372036854775808")>{}; const auto b = vt_ref<big_t>(r.src); const auto len = static_cast<std::size_t>(std::sprintf(buffer.data(), "%lld", b)); assert(len <= buffer.size()); auto new_string = new vm_string_t{ len, reinterpret_cast<uint8_t *>(buffer.data()) }; dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); pt_ref(r.dest) = new_string->get_allocation(); } // // Arithmetic operations // EXEC_DECL(negf) { vt_ref<real_t>(r.dest) = -vt_ref<real_t>(r.src); } EXEC_DECL(addb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) + vt_ref<byte_t>(r.mid); } EXEC_DECL(addw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) + vt_ref<word_t>(r.mid); } EXEC_DECL(addl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) + vt_ref<big_t>(r.mid); } EXEC_DECL(addf) { vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.src) + vt_ref<real_t>(r.mid); } EXEC_DECL(subb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.mid) - vt_ref<byte_t>(r.src); } EXEC_DECL(subw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.mid) - vt_ref<word_t>(r.src); } EXEC_DECL(subl) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) - vt_ref<big_t>(r.src); } EXEC_DECL(subf) { vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.mid) - vt_ref<real_t>(r.src); } EXEC_DECL(mulb) { vt_ref<byte_t>(r.dest) = vt_ref<byte_t>(r.src) * vt_ref<byte_t>(r.mid); } EXEC_DECL(mulw) { vt_ref<word_t>(r.dest) = vt_ref<word_t>(r.src) * vt_ref<word_t>(r.mid); } EXEC_DECL(mull) { vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.src) * vt_ref<big_t>(r.mid); } EXEC_DECL(mulf) { vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.src) * vt_ref<real_t>(r.mid); } template<typename PrimitiveType> void _div(vm_registers_t &r) { const auto denom = vt_ref<PrimitiveType>(r.src); if (denom == PrimitiveType{ 0 }) throw divide_by_zero{}; vt_ref<PrimitiveType>(r.dest) = vt_ref<PrimitiveType>(r.mid) / denom; } // [SPEC] The C++ defines division by 0 as undefined, however the IEEE standard does // define the behavior. Therefore the behavior is platform dependent and controlled // by the built-in Math module. template<> void _div<real_t>(vm_registers_t &r) { const auto denom = vt_ref<real_t>(r.src); vt_ref<real_t>(r.dest) = vt_ref<real_t>(r.mid) / denom; } EXEC_DECL(divb) { _div<byte_t>(r); } EXEC_DECL(divw) { _div<word_t>(r); } EXEC_DECL(divf) { _div<real_t>(r); } EXEC_DECL(divl) { _div<big_t>(r); } template<typename PrimitiveType> void _mod(vm_registers_t &r) { const auto denom = vt_ref<PrimitiveType>(r.src); if (denom == PrimitiveType{ 0 }) throw divide_by_zero{}; vt_ref<PrimitiveType>(r.dest) = vt_ref<PrimitiveType>(r.mid) % denom; } EXEC_DECL(modb) { _mod<byte_t>(r); } EXEC_DECL(modw) { _mod<word_t>(r); } EXEC_DECL(modl) { _mod<big_t>(r); } // [SPEC] 'Exponent' op code // Undocumented: (http://www.vitanuova.com/inferno/papers/dis.html) // Limbo reference: (http://www.vitanuova.com/inferno/papers/addendum.pdf) // // Inferno implementation: (libinterp/xec.c) // src1 = power // src2 = base // // exp[w|l|f] src1 src2 dst // dst = src2 'raised to' src1 // // Result type matches the type of base. Power should be type 'word'. template<typename PrimitiveType> void _exp(vm_registers_t &r) { auto base = vt_ref<PrimitiveType>(r.mid); auto power = vt_ref<word_t>(r.src); auto inverse = false; if (power < 0) { power = (-power); inverse = true; } auto result = PrimitiveType{ 1 }; for (;;) { if ((power & 1) != 0) result *= base; power >>= 1; if (power == 0) break; base *= base; } if (inverse) result = (1 / result); vt_ref<PrimitiveType>(r.dest) = result; } EXEC_DECL(expw) { _exp<word_t>(r); } EXEC_DECL(expl) { _exp<big_t>(r); } EXEC_DECL(expf) { _exp<real_t>(r); } // [SPEC] 'Fixed point' op codes // Undocumented: (http://www.vitanuova.com/inferno/papers/dis.html) // Limbo reference: (http://www.vitanuova.com/inferno/papers/addendum.pdf) // Fixed point identities // X = s * x // Y = t * y // Z = u * z // // Multiplication: // uz = sx * ty // z = (sx/u) * (ty/u) // z = (st/u) * (xy) // // Division: // uz = sx/ty // z = (sx/ty) * (1/u) // z = (s/tu) * (x/y) // Multiply for fixed point types with final scaling as a power of 2. // mulx src1 src2 dst // src1 - integer 1 (y) // src2 - integer 2 (x) // fpr_2 - result scaling factor (2^p) // dest - result integer (z) EXEC_DECL(mulx) { const auto x = big_t{ vt_ref<word_t>(r.mid) }; const auto y = big_t{ vt_ref<word_t>(r.src) }; auto z = x * y; const auto pow2_scale = *reinterpret_cast<word_t *>(r.stack.peek_frame()->fixed_point_register_2()); if (pow2_scale >= 0) z <<= pow2_scale; else z >>= (-pow2_scale); vt_ref<word_t>(r.dest) = static_cast<word_t>(z); } // Division for fixed point types with final scaling as a power of 2. // divx src1 src2 dst // src1 - integer 1 (y) // src2 - integer 2 (x) // fpr_2 - result scaling factor (2^p) // dest - result integer (z) EXEC_DECL(divx) { auto x = big_t{ vt_ref<word_t>(r.mid) }; const auto pow2_scale = *reinterpret_cast<word_t *>(r.stack.peek_frame()->fixed_point_register_2()); if (pow2_scale >= 0) x <<= pow2_scale; else x >>= (-pow2_scale); const auto y = big_t{ vt_ref<word_t>(r.src) }; if (y == 0) throw divide_by_zero{}; const auto z = x / y; vt_ref<word_t>(r.dest) = static_cast<word_t>(z); } // mulx0 src1 src2 dst // src1 - integer 1 (y) // src2 - integer 2 (x) // fpr_1 - residual factor // fpr_2 - result scaling factor (2^p) // dest - result integer (z) EXEC_DECL(mulx0) { const auto x = big_t{ vt_ref<word_t>(r.mid) }; const auto y = big_t{ vt_ref<word_t>(r.src) }; if (x == 0 || y == 0) { vt_ref<word_t>(r.dest) = 0; return; } auto tmp = x * y; const auto frame = r.stack.peek_frame(); const auto pow2_scale = *reinterpret_cast<word_t *>(frame->fixed_point_register_2()); if (pow2_scale >= 0) tmp <<= pow2_scale; else tmp >>= (-pow2_scale); const auto residual_scale = big_t{ *reinterpret_cast<word_t *>(frame->fixed_point_register_1()) }; assert(residual_scale != 0); const auto z = tmp / residual_scale; vt_ref<word_t>(r.dest) = static_cast<word_t>(z); } // divx0 src1 src2 dst // src1 - integer 1 (y) // src2 - integer 2 (x) // fpr_1 - residual factor // fpr_2 - result scaling factor (2^p) // dest - result integer (z) EXEC_DECL(divx0) { const auto x = big_t{ vt_ref<word_t>(r.mid) }; const auto y = big_t{ vt_ref<word_t>(r.src) }; if (y == 0) throw divide_by_zero{}; if (x == 0) { vt_ref<word_t>(r.dest) = 0; return; } const auto frame = r.stack.peek_frame(); const auto residual_scale = big_t{ *reinterpret_cast<word_t *>(frame->fixed_point_register_1()) }; auto tmp = x * residual_scale; const auto pow2_scale = *reinterpret_cast<word_t *>(frame->fixed_point_register_2()); if (pow2_scale >= 0) tmp <<= pow2_scale; else tmp >>= (-pow2_scale); const auto z = tmp / y; vt_ref<word_t>(r.dest) = static_cast<word_t>(z); } // // Move operations //