1969 lines · 64.4 KB · C++
RawDownload
    void _mov_<vm_alloc_t>(pointer_t dest, pointer_t src, const type_descriptor_t *)    {        auto alloc_prev = at_val<vm_alloc_t>(dest);        dec_ref_count_and_free(alloc_prev);         auto alloc = at_val<vm_alloc_t>(src);        if (alloc != nullptr)        {            alloc->add_ref();            pt_ref(dest) = alloc->get_allocation();        }        else        {            pt_ref(dest) = nullptr;        }    }     EXEC_DECL(movb) { _mov_<byte_t>(r.dest, r.src, nullptr); }    EXEC_DECL(movw) { _mov_<word_t>(r.dest, r.src, nullptr); }    EXEC_DECL(movf) { _mov_<real_t>(r.dest, r.src, nullptr); }    EXEC_DECL(movl) { _mov_<big_t>(r.dest, r.src, nullptr); }    EXEC_DECL(movp) { _mov_<vm_alloc_t>(r.dest, r.src, nullptr); }     EXEC_DECL(movm)    {        assert(r.src != nullptr && r.dest != nullptr);         std::memmove(r.dest, r.src, vt_ref<word_t>(r.mid));    }     EXEC_DECL(movmp)    {        auto type_id = vt_ref<word_t>(r.mid);        const auto &types = r.module_ref->type_section;        assert(0 <= type_id && type_id < static_cast<word_t>(types.size()));        auto type = types[type_id];         disvm::runtime::inc_ref_count_in_memory(*type, r.src);         // Decrement allocated pointers in the destination.        destroy_memory(*type, r.dest);         assert(r.src != nullptr && r.dest != nullptr);        std::memmove(r.dest, r.src, type->size_in_bytes);    }     //    // Compare and Branch operations    //     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); }    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); }    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); }    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); }    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); }    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); }     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); }    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); }    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); }    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); }    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); }    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); }     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); }    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); }    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); }    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); }    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); }    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); }     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); }    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); }    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); }    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); }    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); }    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); }     // Define case table constants    // [SPEC]Table format is partially defined in Dis VM specification    //          [element count] ([low value (inc)][high value (exc)][program counter])* [fallback program counter]    struct case_op_constants    {        static const auto table_length_index = std::size_t{ 0 };        static const auto first_entry_index = std::size_t{ 1 };        static const auto entry_low_index = std::size_t{ 0 };        static const auto entry_high_index = std::size_t{ 1 };        static const auto entry_pc_index = std::size_t{ 2 };        static const auto entry_length = std::size_t{ 3 };    };     template<typename PrimitiveType>    void _case(vm_registers_t &r)    {        const auto value = vt_ref<PrimitiveType>(r.src);         assert(r.dest != nullptr);        const auto table = reinterpret_cast<const PrimitiveType *>(r.dest);        auto entry_count = table[case_op_constants::table_length_index];        assert(entry_count < std::numeric_limits<word_t>::max());         // Initialize the target PC with the fallback value.        // This value is located after all entries in the table.        auto target_pc = table[case_op_constants::first_entry_index + (entry_count * case_op_constants::entry_length)];         // Move to first entry        auto current_entry = table + case_op_constants::first_entry_index;         // Find the matching table entry        for (; 0 < entry_count; --entry_count)        {            // Check if value is in range            if (value < current_entry[case_op_constants::entry_low_index] || current_entry[case_op_constants::entry_high_index] <= value)            {                current_entry += case_op_constants::entry_length;                continue;            }             target_pc = current_entry[case_op_constants::entry_pc_index];            break;        }         assert(0 <= static_cast<std::size_t>(target_pc) && static_cast<std::size_t>(target_pc) < r.module_ref->code_section.size());        r.pc = static_cast<vm_pc_t>(target_pc);    }     EXEC_DECL(casew) { _case<word_t>(r); }    EXEC_DECL(casel) { _case<big_t>(r); }     // [SPEC] The documentation for the 'casec' instruction is a bit sparse    // The full semantics are the case table should have string cases lexically    // ordered (i.e. e[n] < e[n + 1]).    EXEC_DECL(casec)    {        const auto value = at_val<vm_string_t>(r.src);         assert(r.dest != nullptr);        const auto table = reinterpret_cast<const word_t *>(r.dest);        const auto entry_count = table[case_op_constants::table_length_index];         // Initialize the target PC with the fallback value.         // This value is located after all entries in the table.        auto target_pc = table[case_op_constants::first_entry_index + (entry_count * case_op_constants::entry_length)];         // Move to first entry        auto current_entry = table + case_op_constants::first_entry_index;         // Find the matching table entry        for (auto entry = word_t{ 0 }; entry < entry_count; ++entry)        {            auto string_low = at_val<vm_string_t>(reinterpret_cast<pointer_t>(const_cast<word_t *>(current_entry + case_op_constants::entry_low_index)));             // Compare the value against the low string            int result = vm_string_t::compare(value, string_low);            if (result != 0)            {                // If the value compares less than the low string then skip the high string.                if (result < 0)                {                    current_entry += case_op_constants::entry_length;                    continue;                }                 // Compare the value against the high string                auto string_high = at_val<vm_string_t>(reinterpret_cast<pointer_t>(const_cast<word_t *>(current_entry + case_op_constants::entry_high_index)));                if (string_high == nullptr || vm_string_t::compare(value, string_high) != 0)                {                    current_entry += case_op_constants::entry_length;                    continue;                }            }             target_pc = current_entry[case_op_constants::entry_pc_index];            break;        }         assert(0 <= static_cast<std::size_t>(target_pc) && static_cast<std::size_t>(target_pc) < r.module_ref->code_section.size());        r.pc = target_pc;    }     //    // String operations    //     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); }    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); }    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); }    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); }    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); }    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); }     EXEC_DECL(lenc)    {        auto len = word_t{ 0 };        auto str = at_val<vm_string_t>(r.src);        if (str != nullptr)            len = str->get_length();         vt_ref<word_t>(r.dest) = len;    }     EXEC_DECL(indc)    {        auto str = at_val<vm_string_t>(r.src);        if (str == nullptr)            throw dereference_nil{ "Index string" };         const auto index = vt_ref<word_t>(r.mid);        const auto rune = str->get_rune(index);         vt_ref<rune_t>(r.dest) = rune;    }     // [SPEC] 'Insert Character' op code    // Documented: (http://www.vitanuova.com/inferno/papers/dis.html)    //   insc src1 src2 dst    //   src1[src2] = dst    //    // Inferno implementation: (libinterp/string.c)    //   insc src1 src2 dst    //   dst[src2] = src1    EXEC_DECL(insc)    {        auto rune = vt_ref<rune_t>(r.src);        auto index = vt_ref<word_t>(r.mid);        auto str = at_val<vm_string_t>(r.dest);        if (str == nullptr)            str = new vm_string_t();        else if (str->get_ref_count() > 1)            str = new vm_string_t(*str, 0, str->get_length());         // Set the rune at the supplied index.        str->set_rune(index, rune);         auto old_str = at_val<vm_string_t>(r.dest);        if (str != old_str)        {            pt_ref(r.dest) = str->get_allocation();            dec_ref_count_and_free(old_str);        }    }     vm_string_t *_addc(vm_string_t *s1, vm_string_t *s2, /* in out */ bool &append_to_s1)    {        if (s1 == nullptr)        {            if (s2 == nullptr)                return new vm_string_t();             return new vm_string_t(*s2, 0, s2->get_length());        }         // There are other references to this string so we will not alter it.        if (s1->get_ref_count() > 1)            append_to_s1 = false;         if (s2 == nullptr)        {            if (append_to_s1)                return s1;             return new vm_string_t(*s1, 0, s1->get_length());        }         return (append_to_s1) ? &(s1->append(*s2)) : new vm_string_t(*s1, *s2);    }     // [SPEC] 'Concat strings' op code    // Documented: (http://www.vitanuova.com/inferno/papers/dis.html)    //   addc src1 src2 dst    //   dst = src1 + src2    //    // Inferno implementation: (libinterp/string.c)    //   addc src1 src2 dst    //   dst = src2 + src1    EXEC_DECL(addc)    {        auto s1 = at_val<vm_string_t>(r.mid);        auto s2 = at_val<vm_string_t>(r.src);        auto dest_maybe = at_val<vm_string_t>(r.dest);         // If the source is the same as the destination and the        // destination is not null, append the string. This can         // change during the concat process.        auto append_to_source = dest_maybe != nullptr && s1 == dest_maybe;        auto str = _addc(s1, s2, append_to_source);         if (!append_to_source)        {            pt_ref(r.dest) = str->get_allocation();            dec_ref_count_and_free(dest_maybe);        }    }     EXEC_DECL(slicec)    {        const auto start = vt_ref<word_t>(r.src);        const auto end = vt_ref<word_t>(r.mid);        auto str = at_val<vm_string_t>(r.dest);         if (str != nullptr)        {            auto new_string = new vm_string_t(*str, start, end);            pt_ref(r.dest) = new_string->get_allocation();        }        else if (start == 0 && end == 0)        {            pt_ref(r.dest) = nullptr;        }        else        {            throw dereference_nil{ "Slice a nil string" };        }         dec_ref_count_and_free(str);    }     //    // Array operations    //     EXEC_DECL(lena)    {        auto len = word_t{ 0 };        auto arr = at_val<vm_array_t>(r.src);        if (arr != nullptr)            len = arr->get_length();         vt_ref<word_t>(r.dest) = len;    }     EXEC_DECL(slicea)    {        const auto begin_index = vt_ref<word_t>(r.src);        const auto end_index = vt_ref<word_t>(r.mid);        const auto length = end_index - begin_index;         auto arr = at_val<vm_array_t>(r.dest);        if (arr == nullptr)        {            if (length == 0)                return;             throw vm_user_exception{ "Slice of empty array is invalid" };        }        else if (begin_index < 0 || arr->get_length() < end_index || length < 0)        {            throw out_of_range_memory{};        }         auto new_array = new vm_array_t(arr, begin_index, length);         if (arr->alloc_type->map_in_bytes > 0)            vm.get_garbage_collector().track_allocation(new_array);         pt_ref(r.dest) = new_array->get_allocation();    }     EXEC_DECL(slicela)    {        auto src_arr = at_val<vm_array_t>(r.src);        if (src_arr == nullptr)            return;         auto dest_arr = at_val<vm_array_t>(r.dest);        if (dest_arr == nullptr)            throw dereference_nil{ "Slice assignment" };         const auto begin_index = vt_ref<word_t>(r.mid);        dest_arr->copy_from(*src_arr, begin_index);    }     void _index_in(vm_registers_t &r)    {        auto arr = at_val<vm_array_t>(r.src);        if (arr == nullptr)            throw dereference_nil{ "Indexing into array" };         auto index = vt_ref<word_t>(r.dest);        if (index < 0 || arr->get_length() <= index)            throw index_out_of_range_memory{ 0, arr->get_length(), index };         // Return the address, not the value        pt_ref(r.mid) = arr->at(index);    }     EXEC_DECL(indb) { _index_in(r); }    EXEC_DECL(indw) { _index_in(r); }    EXEC_DECL(indf) { _index_in(r); }    EXEC_DECL(indl) { _index_in(r); }    EXEC_DECL(indx) { _index_in(r); }     //    // List operations    //     EXEC_DECL(lenl)    {        auto len = word_t{ 0 };        auto list = at_val<vm_list_t>(r.src);        if (list != nullptr)        {            assert(list->alloc_type == vm_list_t::type_desc());            len = list->get_length();        }         vt_ref<word_t>(r.dest) = len;    }     template<typename PrimitiveType>    void _cons(vm_registers_t &r)    {        auto tail_maybe = at_val<vm_list_t>(r.dest);        auto new_list = new vm_list_t{ intrinsic_type_desc::type<PrimitiveType>(), tail_maybe };         if (tail_maybe != nullptr)            tail_maybe->release();         auto value = vt_ref<PrimitiveType>(r.src);        vt_ref<PrimitiveType>(new_list->value()) = value;         pt_ref(r.dest) = new_list->get_allocation();    }     EXEC_DECL(consb) { _cons<byte_t>(r); }    EXEC_DECL(consw) { _cons<word_t>(r); }    EXEC_DECL(consf) { _cons<real_t>(r); }    EXEC_DECL(consl) { _cons<big_t>(r); }     EXEC_DECL(consp)    {        auto tail_maybe = at_val<vm_list_t>(r.dest);         // Create a new list        auto new_list = new vm_list_t{ intrinsic_type_desc::type<pointer_t>(), tail_maybe };        if (tail_maybe != nullptr)            tail_maybe->release();         auto alloc = at_val<vm_alloc_t>(r.src);        if (alloc != nullptr)        {            alloc->add_ref();             pt_ref(new_list->value()) = alloc->get_allocation();             vm.get_garbage_collector().track_allocation(new_list);        }        else        {            pt_ref(new_list->value()) = nullptr;        }         // Return the list        pt_ref(r.dest) = new_list->get_allocation();    }     EXEC_DECL(consmp)    {        auto type_id = vt_ref<word_t>(r.mid);        const auto &types = r.module_ref->type_section;        assert(0 <= type_id && type_id < static_cast<word_t>(types.size()));        auto type = types[type_id];         // Increment the ref count for element content        inc_ref_count_in_memory(*type, r.src);         auto tail_maybe = at_val<vm_list_t>(r.dest);         // Create a new list        auto new_list = new vm_list_t{ type, tail_maybe };        if (tail_maybe != nullptr)            tail_maybe->release();         auto destination = new_list->value();        std::memmove(destination, r.src, type->size_in_bytes);         if (type->map_in_bytes > 0)            vm.get_garbage_collector().track_allocation(new_list);         // Return the list        pt_ref(r.dest) = new_list->get_allocation();    }     template<typename PrimitiveType>    void _head(vm_registers_t &r)    {        auto head = at_val<vm_list_t>(r.src);        if (head == nullptr)            throw dereference_nil{ "Head of list" };         assert(head->get_element_type() == intrinsic_type_desc::type<PrimitiveType>());        auto head_value = head->value();        vt_ref<PrimitiveType>(r.dest) = vt_ref<PrimitiveType>(head_value);    }     EXEC_DECL(headb) { _head<byte_t>(r); }    EXEC_DECL(headw) { _head<word_t>(r); }    EXEC_DECL(headf) { _head<real_t>(r); }    EXEC_DECL(headl) { _head<big_t>(r); }     EXEC_DECL(headp)

Lines 501–1000 of 1969. Previous lines