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 Load more
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)