2035 lines · 67.3 KB · C++
RawDownload
//// 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.