305 lines · 9.9 KB · C++
RawDownload
//// Dis VM// File: stack.cpp// Author: arr// #include <cinttypes>#include <runtime.h>#include <builtin_module.h>#include <vm_memory.h>#include <debug.h>#include <exceptions.h> using disvm::debug::component_trace_t;using disvm::debug::log_level_t; using disvm::runtime::type_descriptor_t;using disvm::runtime::pointer_t;using disvm::runtime::vm_pc_t;using disvm::runtime::vm_module_ref_t;using disvm::runtime::vm_frame_t;using disvm::runtime::vm_stack_t;using disvm::runtime::vm_alloc_t; namespace{    struct vm_stack_page    {        vm_stack_page *prev_page;        std::uintptr_t page_limit_addr;        vm_frame_t *page_top_frame; // Do not free. This is used as a tracking pointer.         void *stack_data() const        {            // The beginning of the stack data is at the end of the struct.            return reinterpret_cast<uint8_t *>(const_cast<vm_stack_page *>(this)) + sizeof(vm_stack_page);        }         bool contains_frame(const vm_frame_t *frame) const        {            return reinterpret_cast<std::uintptr_t>(stack_data()) <= reinterpret_cast<std::uintptr_t>(frame) && reinterpret_cast<std::uintptr_t>(frame) < page_limit_addr;        }    };     struct vm_stack_layout final : public vm_alloc_t    {        static std::shared_ptr<const type_descriptor_t> type_desc()        {            static auto td = type_descriptor_t::create(0);            return td;        }         vm_stack_layout(std::size_t stack_page_size)            : vm_alloc_t(type_desc())            , stack_page_size{ stack_page_size }            , top_frame{ nullptr }            , top_page{ nullptr }        {            new_page();        }         ~vm_stack_layout()        {            assert(top_page != nullptr);            disvm::runtime::free_memory(top_page);        }         const std::size_t stack_page_size;         vm_stack_page *top_page;        vm_frame_t *top_frame; // Do not free. This is used as a tracking pointer.         // Create a new 'top page' and return stack data address        void *new_page()        {            auto current_stack_page = top_page;            top_page = disvm::runtime::alloc_memory<vm_stack_page>(sizeof(vm_stack_page) + stack_page_size);             // Initialize the stack allocation.            top_page->prev_page = current_stack_page;             // The top frame member is only for tracking and so is initialized to null.            top_page->page_top_frame = nullptr;            top_page->page_limit_addr = reinterpret_cast<std::uintptr_t>(top_page)+stack_page_size;             if (disvm::debug::is_component_tracing_enabled<component_trace_t::memory>())                disvm::debug::log_msg(component_trace_t::memory, log_level_t::debug, "alloc: vm stack alloc: %#" PRIxPTR " %#"  PRIxPTR, top_page, top_page->page_limit_addr);             return top_page->stack_data();        }         // Drop the current page and move to the previous one        void drop_page()        {            auto current_stack_page = top_page->prev_page;            assert(current_stack_page != nullptr);             disvm::runtime::free_memory(top_page);             if (disvm::debug::is_component_tracing_enabled<component_trace_t::memory>())                disvm::debug::log_msg(component_trace_t::memory, log_level_t::debug, "free: vm stack alloc");             // Set the top page            top_page = current_stack_page;        }    };} vm_frame_t::vm_frame_t(std::shared_ptr<const type_descriptor_t> td)    : frame_type{ std::move(td) }{    assert(frame_type != nullptr);     // Initialize the new frame    init_memory(*frame_type, base());     prev_pc() = runtime_constants::invalid_program_counter;    prev_frame() = nullptr;    prev_module_ref() = nullptr;     if (disvm::debug::is_component_tracing_enabled<component_trace_t::memory>())        disvm::debug::log_msg(component_trace_t::memory, log_level_t::debug, "init: vm frame: %#" PRIxPTR " %d", this, frame_type->size_in_bytes);} vm_frame_t::~vm_frame_t(){    dec_ref_count_and_free(prev_module_ref());     destroy_memory(*frame_type, base());     if (disvm::debug::is_component_tracing_enabled<component_trace_t::memory>())        disvm::debug::log_msg(component_trace_t::memory, log_level_t::debug, "destroy: vm frame: %#" PRIxPTR, this);} void vm_frame_t::copy_frame_contents(const vm_frame_t &other){    auto this_frame_base = reinterpret_cast<uint8_t *>(base());    auto this_frame_contents = this_frame_base + sizeof(vm_frame_base_alloc_t);     auto other_frame_base = reinterpret_cast<uint8_t *>(other.base());    auto other_frame_contents = other_frame_base + sizeof(vm_frame_base_alloc_t);     assert(frame_type->size_in_bytes > sizeof(vm_frame_base_alloc_t));    std::memcpy(this_frame_contents, other_frame_contents, frame_type->size_in_bytes - sizeof(vm_frame_base_alloc_t));     inc_ref_count_in_memory(*frame_type, this_frame_base);} pointer_t vm_frame_t::base() const{    // The beginning of the frame data is at the end of the frame struct.    return reinterpret_cast<pointer_t>(reinterpret_cast<uint8_t *>(const_cast<vm_frame_t *>(this)) + sizeof(vm_frame_t));} vm_pc_t &vm_frame_t::prev_pc() const{    return base<vm_frame_base_alloc_t>().p_pc;} vm_frame_t *&vm_frame_t::prev_frame() const{    return base<vm_frame_base_alloc_t>().p_fp;} vm_module_ref_t *&vm_frame_t::prev_module_ref() const{    return base<vm_frame_base_alloc_t>().p_mr;} pointer_t vm_frame_t::fixed_point_register_1() const{    const auto offset = vm_frame_constants::fixed_point_register_1_offset();    assert(offset < frame_type->size_in_bytes);     auto p = reinterpret_cast<uint8_t *>(base()) + offset;    return reinterpret_cast<pointer_t>(p);} pointer_t vm_frame_t::fixed_point_register_2() const{    const auto offset = vm_frame_constants::fixed_point_register_2_offset();    assert(offset < frame_type->size_in_bytes);     auto p = reinterpret_cast<uint8_t *>(base()) + offset;    return reinterpret_cast<pointer_t>(p);} vm_stack_t::vm_stack_t(std::size_t stack_extent)    : _mem{ std::make_unique<vm_stack_layout>(stack_extent) }{    assert(0 < stack_extent && stack_extent < static_cast<std::size_t>(std::numeric_limits<word_t>::max()));    log_msg(component_trace_t::memory, log_level_t::debug, "init: vm stack");} vm_stack_t::~vm_stack_t(){    while (nullptr != pop_frame())    {        // Pop all frames off the stack    }     // Free stack memory    _mem->release();    _mem.reset();     log_msg(component_trace_t::memory, log_level_t::debug, "destroy: vm stack");} vm_frame_t *vm_stack_t::alloc_frame(std::shared_ptr<const type_descriptor_t> frame_type){    assert(frame_type != nullptr);    assert(sizeof(vm_frame_base_alloc_t) < frame_type->size_in_bytes && "Requested frame size less than VM frame base size");     auto layout = static_cast<vm_stack_layout *>(_mem.get());     auto page_top_frame = layout->top_page->page_top_frame;    const auto new_frame_size = sizeof(vm_frame_t) + frame_type->size_in_bytes;    if (layout->stack_page_size < new_frame_size)        throw vm_system_exception{ "Requested stack frame larger than stack page" };     vm_frame_t *new_frame;    if (page_top_frame == nullptr)    {        auto new_frame_addr = layout->top_page->stack_data();        new_frame = ::new(new_frame_addr)vm_frame_t{ std::move(frame_type) };    }    else    {        auto current_page_top_frame_addr = reinterpret_cast<std::uintptr_t>(page_top_frame);        auto new_frame_addr_maybe = current_page_top_frame_addr + (sizeof(vm_frame_t) + page_top_frame->frame_type->size_in_bytes);         // If the new frame is not going to fit on the current page, create a new page.        if ((new_frame_addr_maybe + new_frame_size) >= layout->top_page->page_limit_addr)            new_frame_addr_maybe = reinterpret_cast<std::uintptr_t>(layout->new_page());         new_frame = ::new(reinterpret_cast<void *>(new_frame_addr_maybe))vm_frame_t{ std::move(frame_type) };    }     // Record the top frame on the page    layout->top_page->page_top_frame = new_frame;     return new_frame;} vm_frame_t *vm_stack_t::push_frame(){    auto layout = static_cast<vm_stack_layout *>(_mem.get());     auto current_top_frame = layout->top_frame;    auto new_top_frame = layout->top_page->page_top_frame;    new_top_frame->prev_frame() = current_top_frame;     layout->top_frame = new_top_frame;     if (disvm::debug::is_component_tracing_enabled<component_trace_t::stack>())        log_msg(component_trace_t::stack, log_level_t::debug, "update: push vm frame: %#" PRIxPTR, new_top_frame);     return new_top_frame;} vm_frame_t *vm_stack_t::pop_frame(){    auto layout = static_cast<vm_stack_layout *>(_mem.get());     auto current_frame = layout->top_frame;    if (current_frame == nullptr)        return nullptr;     {        // Dispose of the current top frame and update the current frame.        auto previous_frame = current_frame;        current_frame = previous_frame->prev_frame();         // Call destructor on previous frame since we allocated        // the frame on the stack page.        previous_frame->~vm_frame_t();    }     layout->top_frame = current_frame;    layout->top_page->page_top_frame = current_frame;     // Check if the stack page is no longer needed.    if (current_frame != nullptr)    {        // Clean up the stack page allocation if the current frame is on a previous page        if (!layout->top_page->contains_frame(current_frame))        {            layout->drop_page();             // Ensure the current frame is in the new page.            assert(layout->top_page->contains_frame(current_frame));        }    }     if (disvm::debug::is_component_tracing_enabled<component_trace_t::stack>())        log_msg(component_trace_t::stack, log_level_t::debug, "update: pop vm frame: %#" PRIxPTR, current_frame);     return current_frame;} vm_frame_t *vm_stack_t::peek_frame() const{    auto layout = static_cast<vm_stack_layout *>(_mem.get());    return layout->top_frame;}