const auto function_pc = function_ref.entry_pc; assert(static_cast<std::size_t>(function_pc) < target_module->code_section.size()); // Push the next frame auto top_frame = r.stack.push_frame(); #ifndef NDEBUG // Validate the stack state const auto frame_base_pointer = pt_ref(r.src); assert(frame_base_pointer != nullptr); assert(top_frame->base() == frame_base_pointer);#endif // Store current state top_frame->prev_pc() = r.pc; top_frame->prev_module_ref() = r.module_ref; if (debug::is_component_tracing_enabled<debug::component_trace_t::stack>()) { debug::log_msg( debug::component_trace_t::stack, debug::log_level_t::debug, "enter: function: inter-module: %d >>%s<< %d %s\n", export_id, target_module->module->module_name->str(), function_pc, (target_module->is_builtin_module() ? "true" : "false")); } // Set registers for execution r.module_ref = target_module; r.module_ref->add_ref(); r.mp_base = r.module_ref->mp_base; r.pc = function_pc; if (r.module_ref->is_builtin_module()) { const auto &inst = r.module_ref->code_section[function_pc]; assert(r.mp_base == nullptr && "Built-in modules shouldn't have module data (MP register)"); r.current_thread_state = vm_thread_state_t::release; inst.native(r, vm); ret(r, vm); // Only reset the thread if it is in the state set prior to native call. if (r.current_thread_state == vm_thread_state_t::release) r.current_thread_state = vm_thread_state_t::running; } else { r.mp_base->add_ref(); } } EXEC_DECL(jmp) { r.pc = vt_ref<vm_pc_t>(r.dest); } EXEC_DECL(goto_) { auto pc_index = vt_ref<word_t>(r.src); assert(r.dest != nullptr); auto pc_table = reinterpret_cast<vm_pc_t *>(r.dest); r.pc = pc_table[pc_index]; } // // Thread operations // EXEC_DECL(spawn) { // Arguments to the spawned thread are passed on the current // thread's stack in a new frame. auto spawned_frame = r.stack.push_frame(); #ifndef NDEBUG // Validate the stack state const auto frame_base_pointer = pt_ref(r.src); assert(frame_base_pointer != nullptr); assert(spawned_frame->base() == frame_base_pointer);#endif const auto starting_pc = vt_ref<vm_pc_t>(r.dest); assert(r.module_ref != nullptr); vm.fork(r.thread.get_thread_id(), *r.module_ref, *spawned_frame, starting_pc); // After the new thread is forked pop off the argument passing frame. r.stack.pop_frame(); } EXEC_DECL(mspawn) { // Arguments to the spawned thread are passed on the current // thread's stack in a new frame. auto spawned_frame = r.stack.push_frame(); #ifndef NDEBUG // Validate the stack state const auto frame_base_pointer = pt_ref(r.src); assert(frame_base_pointer != nullptr); assert(spawned_frame->base() == frame_base_pointer);#endif const auto import_function_id = vt_ref<word_t>(r.mid); auto target_module_ref = at_val<vm_module_ref_t>(r.dest); assert(target_module_ref != nullptr); const auto &function_ref = target_module_ref->get_function_ref(import_function_id); // Spawning a built-in module is not permitted if (target_module_ref->is_builtin_module()) throw vm_user_exception{ "Spawning a built-in module is not permitted" }; vm.fork(r.thread.get_thread_id(), *target_module_ref, *spawned_frame, function_ref.entry_pc); // After the new thread is forked pop off the argument passing frame. r.stack.pop_frame(); } EXEC_DECL(exit) { r.current_thread_state = vm_thread_state_t::exiting; } // // Exception operations // EXEC_DECL(raise) { auto e = at_val<vm_alloc_t>(r.src); if (e == nullptr) throw dereference_nil{ "Raise exception" }; const vm_string_t *exception_id; // Determine if a 'string' or 'exception' type was raised. if (e->alloc_type == vm_string_t::type_desc()) { // String exception_id = static_cast<vm_string_t *>(e); if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>()) debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "raise: string: >>%s<<\n", exception_id->str()); } else { // [SPEC] The exception format is not formally specified. // The first pointer in the exception is a string type // that is used to uniquely identify the exception type. // The remainder of the type is user-defined. auto content = e->get_allocation(); exception_id = at_val<vm_string_t>(content); if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>()) debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "raise: exception: >>%s<<\n", exception_id->str()); } // Check if the tool dispatcher has been supplied auto tool_dispatch = r.tool_dispatch.load(); if (tool_dispatch != nullptr) tool_dispatch->on_exception_raised(r, *exception_id, *e); const handler_t *handler; vm_frame_t *target_frame; vm_pc_t handler_pc; // The faulting pc is minus one since the VM has already incremented the counter. const auto faulting_pc = r.pc - 1; assert(r.module_ref != nullptr); std::tie(handler, target_frame, handler_pc) = find_exception_handler(r.stack, *r.module_ref, faulting_pc, *exception_id); if (handler == nullptr && target_frame == nullptr && handler_pc == runtime_constants::invalid_program_counter) { if (tool_dispatch != nullptr) tool_dispatch->on_exception_unhandled(r, *exception_id, *e); auto faulting_module_name = r.module_ref->module->module_name->str(); throw unhandled_user_exception{ exception_id->str(), faulting_module_name, faulting_pc }; } assert(target_frame != nullptr); // Now that we know the exception has a handler, take a reference. e->add_ref(); if (target_frame != r.stack.peek_frame()) { while (target_frame != r.stack.pop_frame()) { // Remove frames from the stack until the target frame is found. } } // Re-initialize the current frame if (handler->type_desc != nullptr) { destroy_memory(*handler->type_desc, target_frame->base()); init_memory(*handler->type_desc, target_frame->base()); } // Set the exception at the appropriate frame offset auto frame_pointer = reinterpret_cast<uint8_t *>(target_frame->base()); auto exception_destination = reinterpret_cast<pointer_t>(frame_pointer + handler->exception_offset); // Destroy any data at this location. auto current_value = at_val<vm_alloc_t>(exception_destination); dec_ref_count_and_free(current_value); // Set the exception destination. pt_ref(exception_destination) = e->get_allocation(); r.pc = handler_pc; } // [SPEC] Added to support debugger. // The actual opcode for execution can be retrieved from the tool dispatch interface. // The current register state is for the real opcode, the breakpoint opcode has no inputs/outputs. EXEC_DECL(brkpt) { auto tool_dispatch = r.tool_dispatch.load(); if (tool_dispatch == nullptr) { assert(false && "Breakpoint set but no tool dispatcher found"); throw vm_system_exception{ "Breakpoint set but no tool(s) loaded" }; } const auto original_op = tool_dispatch->on_breakpoint(r); // Continue execution vm_exec_table[static_cast<std::size_t>(original_op)](r, vm); } // // Module operations // EXEC_DECL(load) { // Validate module loading module. auto importing_module = r.module_ref->module; if (!util::has_flag(importing_module->header.runtime_flag, runtime_flags_t::has_import)) throw vm_user_exception{ "Invalid importing module" }; auto str = at_val<vm_string_t>(r.src); assert(str != nullptr); dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); pt_ref(r.dest) = nullptr; auto imported_module = std::shared_ptr<vm_module_t>{}; // Handle self loading module optimization. if (std::strcmp(str->str(), "$self") == 0) { // Remove 'const' from the module type in the shared_ptr<> imported_module = std::const_pointer_cast<vm_module_t>(r.module_ref->module); } else { try { imported_module = vm.load_module(str->str()); } catch (const vm_module_exception &e) { debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "exception: load: >>%s<<\n", e.what()); assert(false && "Failure reading module"); return; } } auto module_import_index = vt_ref<uint32_t>(r.mid); const auto &imports = importing_module->import_section; assert(module_import_index < imports.size()); const auto &function_imports = imports[module_import_index]; auto entry_module_ref = new vm_module_ref_t{ imported_module, function_imports }; pt_ref(r.dest) = entry_module_ref->get_allocation(); // Check if the tool dispatcher has been supplied auto tool_dispatch = r.tool_dispatch.load(); if (tool_dispatch != nullptr) tool_dispatch->on_module_thread_load(r, imported_module); }} const vm_exec_t disvm::runtime::vm_exec_table[static_cast<std::size_t>(opcode_t::last_opcode) + 1] ={ invalid, alt, nbalt, goto_, call, frame, spawn, runt, load, mcall, mspawn, mframe, ret, jmp, casew, exit, new_, newa, newcb, newcw, newcf, newcp, newcm, newcmp, send, recv, consb, consw, consp, consf, notimpl, // consm, consmp, headb, headw, headp, headf, notimpl, // headm, headmp, tail, lea, indx, movp, movm, movmp, movb, movw, movf, cvtbw, cvtwb, cvtfw, cvtwf, cvtca, cvtac, cvtwc, cvtcw, cvtfc, cvtcf, addb, addw, addf, subb, subw, subf, mulb, mulw, mulf, divb, divw, divf, modw, modb, andb, andw, orb, orw, xorb, xorw, shlb, shlw, shrb, shrw, insc, indc, addc, lenc, lena, lenl, beqb, bneb, bltb, bleb, bgtb, bgeb, beqw, bnew, bltw, blew, bgtw, bgew, beqf, bnef, bltf, blef, bgtf, bgef, beqc, bnec, bltc, blec, bgtc, bgec, slicea, slicela, slicec, indw, indf, indb, negf, movl, addl, subl, divl, modl, mull, andl, orl, xorl, shll, shrl, bnel, bltl, blel, bgtl, bgel, beql, cvtlf, cvtfl, cvtlw, cvtwl, cvtlc, cvtcl, headl, consl, newcl, casec, indl, notimpl, // movpc, tcmp, mnewz, cvtrf, cvtfr, cvtws, cvtsw, lsrw, lsrl, eclr, newz, newaz, raise, casel, mulx, divx, cvtxx, mulx0, divx0, cvtxx0, notimpl, // mulx1, notimpl, // divx1, notimpl, // cvtxx1, cvtfx, cvtxf, expw, expl, expf, notimpl, // self, brkpt,};Lines 1501–1969 of 1969. Previous lines
const auto function_pc = function_ref.entry_pc; assert(static_cast<std::size_t>(function_pc) < target_module->code_section.size()); // Push the next frame auto top_frame = r.stack.push_frame(); #ifndef NDEBUG // Validate the stack state const auto frame_base_pointer = pt_ref(r.src); assert(frame_base_pointer != nullptr); assert(top_frame->base() == frame_base_pointer); #endif // Store current state top_frame->prev_pc() = r.pc; top_frame->prev_module_ref() = r.module_ref; if (debug::is_component_tracing_enabled<debug::component_trace_t::stack>()) { debug::log_msg( debug::component_trace_t::stack, debug::log_level_t::debug, "enter: function: inter-module: %d >>%s<< %d %s\n", export_id, target_module->module->module_name->str(), function_pc, (target_module->is_builtin_module() ? "true" : "false")); } // Set registers for execution r.module_ref = target_module; r.module_ref->add_ref(); r.mp_base = r.module_ref->mp_base; r.pc = function_pc; if (r.module_ref->is_builtin_module()) { const auto &inst = r.module_ref->code_section[function_pc]; assert(r.mp_base == nullptr && "Built-in modules shouldn't have module data (MP register)"); r.current_thread_state = vm_thread_state_t::release; inst.native(r, vm); ret(r, vm); // Only reset the thread if it is in the state set prior to native call. if (r.current_thread_state == vm_thread_state_t::release) r.current_thread_state = vm_thread_state_t::running; } else { r.mp_base->add_ref(); } } EXEC_DECL(jmp) { r.pc = vt_ref<vm_pc_t>(r.dest); } EXEC_DECL(goto_) { auto pc_index = vt_ref<word_t>(r.src); assert(r.dest != nullptr); auto pc_table = reinterpret_cast<vm_pc_t *>(r.dest); r.pc = pc_table[pc_index]; } // // Thread operations // EXEC_DECL(spawn) { // Arguments to the spawned thread are passed on the current // thread's stack in a new frame. auto spawned_frame = r.stack.push_frame(); #ifndef NDEBUG // Validate the stack state const auto frame_base_pointer = pt_ref(r.src); assert(frame_base_pointer != nullptr); assert(spawned_frame->base() == frame_base_pointer); #endif const auto starting_pc = vt_ref<vm_pc_t>(r.dest); assert(r.module_ref != nullptr); vm.fork(r.thread.get_thread_id(), *r.module_ref, *spawned_frame, starting_pc); // After the new thread is forked pop off the argument passing frame. r.stack.pop_frame(); } EXEC_DECL(mspawn) { // Arguments to the spawned thread are passed on the current // thread's stack in a new frame. auto spawned_frame = r.stack.push_frame(); #ifndef NDEBUG // Validate the stack state const auto frame_base_pointer = pt_ref(r.src); assert(frame_base_pointer != nullptr); assert(spawned_frame->base() == frame_base_pointer); #endif const auto import_function_id = vt_ref<word_t>(r.mid); auto target_module_ref = at_val<vm_module_ref_t>(r.dest); assert(target_module_ref != nullptr); const auto &function_ref = target_module_ref->get_function_ref(import_function_id); // Spawning a built-in module is not permitted if (target_module_ref->is_builtin_module()) throw vm_user_exception{ "Spawning a built-in module is not permitted" }; vm.fork(r.thread.get_thread_id(), *target_module_ref, *spawned_frame, function_ref.entry_pc); // After the new thread is forked pop off the argument passing frame. r.stack.pop_frame(); } EXEC_DECL(exit) { r.current_thread_state = vm_thread_state_t::exiting; } // // Exception operations // EXEC_DECL(raise) { auto e = at_val<vm_alloc_t>(r.src); if (e == nullptr) throw dereference_nil{ "Raise exception" }; const vm_string_t *exception_id; // Determine if a 'string' or 'exception' type was raised. if (e->alloc_type == vm_string_t::type_desc()) { // String exception_id = static_cast<vm_string_t *>(e); if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>()) debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "raise: string: >>%s<<\n", exception_id->str()); } else { // [SPEC] The exception format is not formally specified. // The first pointer in the exception is a string type // that is used to uniquely identify the exception type. // The remainder of the type is user-defined. auto content = e->get_allocation(); exception_id = at_val<vm_string_t>(content); if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>()) debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "raise: exception: >>%s<<\n", exception_id->str()); } // Check if the tool dispatcher has been supplied auto tool_dispatch = r.tool_dispatch.load(); if (tool_dispatch != nullptr) tool_dispatch->on_exception_raised(r, *exception_id, *e); const handler_t *handler; vm_frame_t *target_frame; vm_pc_t handler_pc; // The faulting pc is minus one since the VM has already incremented the counter. const auto faulting_pc = r.pc - 1; assert(r.module_ref != nullptr); std::tie(handler, target_frame, handler_pc) = find_exception_handler(r.stack, *r.module_ref, faulting_pc, *exception_id); if (handler == nullptr && target_frame == nullptr && handler_pc == runtime_constants::invalid_program_counter) { if (tool_dispatch != nullptr) tool_dispatch->on_exception_unhandled(r, *exception_id, *e); auto faulting_module_name = r.module_ref->module->module_name->str(); throw unhandled_user_exception{ exception_id->str(), faulting_module_name, faulting_pc }; } assert(target_frame != nullptr); // Now that we know the exception has a handler, take a reference. e->add_ref(); if (target_frame != r.stack.peek_frame()) { while (target_frame != r.stack.pop_frame()) { // Remove frames from the stack until the target frame is found. } } // Re-initialize the current frame if (handler->type_desc != nullptr) { destroy_memory(*handler->type_desc, target_frame->base()); init_memory(*handler->type_desc, target_frame->base()); } // Set the exception at the appropriate frame offset auto frame_pointer = reinterpret_cast<uint8_t *>(target_frame->base()); auto exception_destination = reinterpret_cast<pointer_t>(frame_pointer + handler->exception_offset); // Destroy any data at this location. auto current_value = at_val<vm_alloc_t>(exception_destination); dec_ref_count_and_free(current_value); // Set the exception destination. pt_ref(exception_destination) = e->get_allocation(); r.pc = handler_pc; } // [SPEC] Added to support debugger. // The actual opcode for execution can be retrieved from the tool dispatch interface. // The current register state is for the real opcode, the breakpoint opcode has no inputs/outputs. EXEC_DECL(brkpt) { auto tool_dispatch = r.tool_dispatch.load(); if (tool_dispatch == nullptr) { assert(false && "Breakpoint set but no tool dispatcher found"); throw vm_system_exception{ "Breakpoint set but no tool(s) loaded" }; } const auto original_op = tool_dispatch->on_breakpoint(r); // Continue execution vm_exec_table[static_cast<std::size_t>(original_op)](r, vm); } // // Module operations // EXEC_DECL(load) { // Validate module loading module. auto importing_module = r.module_ref->module; if (!util::has_flag(importing_module->header.runtime_flag, runtime_flags_t::has_import)) throw vm_user_exception{ "Invalid importing module" }; auto str = at_val<vm_string_t>(r.src); assert(str != nullptr); dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); pt_ref(r.dest) = nullptr; auto imported_module = std::shared_ptr<vm_module_t>{}; // Handle self loading module optimization. if (std::strcmp(str->str(), "$self") == 0) { // Remove 'const' from the module type in the shared_ptr<> imported_module = std::const_pointer_cast<vm_module_t>(r.module_ref->module); } else { try { imported_module = vm.load_module(str->str()); } catch (const vm_module_exception &e) { debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "exception: load: >>%s<<\n", e.what()); assert(false && "Failure reading module"); return; } } auto module_import_index = vt_ref<uint32_t>(r.mid); const auto &imports = importing_module->import_section; assert(module_import_index < imports.size()); const auto &function_imports = imports[module_import_index]; auto entry_module_ref = new vm_module_ref_t{ imported_module, function_imports }; pt_ref(r.dest) = entry_module_ref->get_allocation(); // Check if the tool dispatcher has been supplied auto tool_dispatch = r.tool_dispatch.load(); if (tool_dispatch != nullptr) tool_dispatch->on_module_thread_load(r, imported_module); } } const vm_exec_t disvm::runtime::vm_exec_table[static_cast<std::size_t>(opcode_t::last_opcode) + 1] = { invalid, alt, nbalt, goto_, call, frame, spawn, runt, load, mcall, mspawn, mframe, ret, jmp, casew, exit, new_, newa, newcb, newcw, newcf, newcp, newcm, newcmp, send, recv, consb, consw, consp, consf, notimpl, // consm, consmp, headb, headw, headp, headf, notimpl, // headm, headmp, tail, lea, indx, movp, movm, movmp, movb, movw, movf, cvtbw, cvtwb, cvtfw, cvtwf, cvtca, cvtac, cvtwc, cvtcw, cvtfc, cvtcf, addb, addw, addf, subb, subw, subf, mulb, mulw, mulf, divb, divw, divf, modw, modb, andb, andw, orb, orw, xorb, xorw, shlb, shlw, shrb, shrw, insc, indc, addc, lenc, lena, lenl, beqb, bneb, bltb, bleb, bgtb, bgeb, beqw, bnew, bltw, blew, bgtw, bgew, beqf, bnef, bltf, blef, bgtf, bgef, beqc, bnec, bltc, blec, bgtc, bgec, slicea, slicela, slicec, indw, indf, indb, negf, movl, addl, subl, divl, modl, mull, andl, orl, xorl, shll, shrl, bnel, bltl, blel, bgtl, bgel, beql, cvtlf, cvtfl, cvtlw, cvtwl, cvtlc, cvtcl, headl, consl, newcl, casec, indl, notimpl, // movpc, tcmp, mnewz, cvtrf, cvtfr, cvtws, cvtsw, lsrw, lsrl, eclr, newz, newaz, raise, casel, mulx, divx, cvtxx, mulx0, divx0, cvtxx0, notimpl, // mulx1, notimpl, // divx1, notimpl, // cvtxx1, cvtfx, cvtxf, expw, expl, expf, notimpl, // self, brkpt, };