YetiVM / code-review

Доброго времени суток.

Есть личный проект - YetiVM (gitlab / github)

Прогнал тесты из RISC-V Architecture Test

Успешно пройдены:

  • rv32i_m/I
  • rv32i_m/M

Так же можно назвать ожидаемым поведением, то что оно выбрасывает исключения на тестах из набора rv32i_m/privilege

Немного о возможных применениях библиотеки:

  • изучение архитектуры RISC-V
  • ASM/C/C++/... в качестве скриптов
  • "поиграться" с самомодифицирующимся кодом
  • ...

Большая просьба - покритиковать код.

Спасибо.

Пример использования(запускатор для тестов):

// @file tests/arch-tests/yeti_runner.cxx
#include <yeti-vm/vm_basic.hxx>

#include <iostream>
#include <cstring>

namespace vm::yeti_runner
{
struct Runner: protected vm::basic_vm
{
    bool initProgram(int testIdx, char ** argv)
    {
        bool isa_ok = init_isa();
        bool mem_ok = init_memory();
        mem_ok = mem_ok && add_memory(std::make_shared<DeviceMemory>(this));

        bool init_ok = isa_ok && mem_ok;
        init_ok = init_ok && initSysCalls();

        auto code = vm::parse_hex(argv[testIdx]);
        init_ok = init_ok && code.has_value();
        init_ok = init_ok && set_program(code.value());

        return init_ok;
    }
    bool initSysCalls()
    {
        syscall_should_throw(false);
        using call = vm::syscall_functor;
        auto& sys = get_syscalls();
        bool ok = sys.register_handler(
                call::create(10, "exit"
                             , [this](vm::MachineInterface* m)
                             { return do_exit(m); }));

        return ok;
    }
    void do_exit(vm::MachineInterface*)
    {
        basic_vm::halt();
    }

    bool exec(bool debug = false)
    {
        enable_debugging(debug);
        start();
        try
        {
            run();
        }
        catch (std::exception& e)
        {
            std::cerr << std::endl << "Exception: " << e.what() << std::endl;
            dump_state(std::cerr);
            return false;
        }
        return !set_dev; // no failures
    }
protected:
    void debug() override
    {
        if (set_dev)
        {
            dump_state(std::cerr);
            auto fill_c = std::cerr.fill();
            std::cerr << std::dec;
            std::cerr << "set_dev == true " << std::endl;
            std::cerr << "DEV MEM: " << std::endl;
            for(auto v: dev_mem)
            {
                std::cerr << "\t" << std::hex << std::setfill('0') << std::setw(8) << v << std::endl;
            }
            std::cerr << "\t:DEV MEM" << std::endl;
            std::cerr << std::dec << std::setfill(fill_c);
            halt();
        }
        return basic_vm::debug();
    }
    void assert_set(uint32_t idx, uint32_t v)
    {
        dev_mem[idx] = v;
        set_dev = true;
    }
    bool set_dev = false;
    std::array<uint32_t, 4> dev_mem{};
protected:
    struct DeviceMemory final: public vm::memory_block
    {
        explicit DeviceMemory(Runner* runner)
            : vm::memory_block{def_data_base + def_data_size, def_data_size}
            , runner{runner} {}

        [[nodiscard]]
        bool load(address_type address, void *dest, size_type size) const final
        {
            std::memset(dest, 0, size);
            return true;
        }

        [[nodiscard]]
        bool store(memory_block::address_type address, const void *source, memory_block::size_type size) final
        {
            runner->set_dev = true;
            if (size != 4) return false;
            auto offset = (address - get_start_address()) / 4;
            if (offset >= runner->dev_mem.size()) return false;
            runner->assert_set(offset, *reinterpret_cast<const uint32_t*>(source));
            return true;
        }

    protected:
        [[nodiscard]]
        const void *get_ro(address_type address, size_type size) const final
        {
            return nullptr;
        }

        [[nodiscard]]
        void *get_rw(address_type address, size_type size) final
        {
            return nullptr;
        }
    private:
        Runner* runner = nullptr;
    };
};
} // vm::yeti_runner

int main(int argc, char ** argv)
{
    int numFails = 0;
    for (int testIdx = 1; testIdx < argc; ++testIdx)
    {
        vm::yeti_runner::Runner yetiVM;
        if (!yetiVM.initProgram(testIdx, argv))
        {
            std::cerr << "Unable init: " << std::dec << testIdx << " " << argv[testIdx] << std::endl;
            return EXIT_FAILURE;
        }
        if (!yetiVM.exec(argc == 2)) // single file - enable debug output
        {
            std::cerr << "Fail: " << std::dec << testIdx << " " << argv[testIdx] << std::endl;
            ++numFails;
        }
    }
    return numFails;
}


Ответы (0 шт):