1969 lines · 64.4 KB · C++
RawDownload
        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