Создание собственного байткода Vm с нуля

Привет всем! В этом уроке я хочу показать вам, как создать свой собственный байткод vm. В этом уроке мы будем создавать регистровый vm. Регистровый vm — это vm, в котором результаты хранятся в определенном регистре.

Что такое байткод vm?

Bytecode vm — это программа, выполняющая двоичный код. Это похоже на то, что делает ваш компьютер, когда вы запускаете программу, но в отличие от родного исполняемого кода он не зависит от машины. Он используется многими языками, потому что это быстрее, чем ходить по асту, чтобы показать результаты, и проще по сравнению с генерацией родного машинного кода.

Давайте начнем

  • Включите необходимые заголовочные файлы
#include <iostream>
#include <vector>
Войдите в полноэкранный режим Выйти из полноэкранного режима
  • Определите регистры, в которых будут сохраняться результаты.
enum Reg{
    r1,
    r2,
    r3,
    r4,
    r5,
    r6,
};
Вход в полноэкранный режим Выход из полноэкранного режима

Вы можете добавить столько, сколько захотите.

  • Определите опкоды, которые поддерживает наш vm. Опкоды — это инструкции, которые указывают, что делать.
enum Opcode{
    OP_PRINT,
    OP_LOAD,
    OP_MOV
};
Войти в полноэкранный режим Выйти из полноэкранного режима

Наш vm очень прост, поэтому в нем всего 3 опкода. Вы должны попробовать добавить свои собственные

  • Фактический код виртуальной машины
class VM{
    private:
        std::vector<int> m_code;//stores the code
        int m_memory[6];//stores the data
    public:
        VM(std::vector<int> code){
            m_code = code;
        }
        void run(){
            size_t pc = 0;
            while(pc < m_code.size()){
                switch(m_code[pc]){
                    case OP_PRINT:{
                        //OP_PRINT <reg>
                        //print the value of the register
                        auto reg=m_code[pc+1];//reg is the register that has the required data
                        auto data=m_memory[reg];//data is the value of the register
                        std::cout << data << std::endl;
                        pc += 2;
                        break;
                    }
                    case OP_LOAD:{
                        //OP_LOAD <reg> <value>
                        //load the value into the register
                        auto reg=m_code[pc+1];//reg is the register where the data will be stored
                        auto data=m_code[pc+2];//data is the value that will be stored
                        m_memory[reg] = data;
                        pc += 3;
                        break;
                    }
                    case OP_MOV:{
                        //OP_MOV <reg1> <reg2>
                        //move the value of the register2 into the register1
                        auto reg1=m_code[pc+1];//reg1 is the register where the data will be stored
                        auto reg2=m_code[pc+2];//reg2 is the register that has the required data
                        m_memory[reg1] = m_memory[reg2];
                        pc += 3;
                        break;
                    }
                    default:
                        std::cout << "Unknown opcode: " << m_code[pc] << std::endl;
                        return;
                }
            }
        }
};
Вход в полноэкранный режим Выйти из полноэкранного режима

ПРИМЕЧАНИЕ:- Эта ВМ предназначена для обучения, поэтому она не очень быстрая и практичная.

  • Код драйвера

int main(){
    std::vector<int> code = {
        OP_LOAD , r1 , 1 ,//$r1 = 1
        OP_LOAD , r2 , 2 ,//$r2 = 2
        OP_MOV , r3 , r1 ,//$r3 = $r1
        OP_PRINT , r3 ,//print $r3
        OP_PRINT , r2 ,//print $r2
        OP_PRINT , r1 ,//print $r1
    };
    auto vm=VM(code);
    vm.run();
}
Вход в полноэкранный режим Выход из полноэкранного режима

Полный код

#include <iostream>
#include <vector>

enum Reg{
    r1,
    r2,
    r3,
    r4,
    r5,
    r6,
};

enum Opcode{
    OP_PRINT,
    OP_LOAD,
    OP_MOV
};

class VM{
    private:
        std::vector<int> m_code;//stores the code
        int m_memory[6];//stores the data
    public:
        VM(std::vector<int> code){
            m_code = code;
        }
        void run(){
            size_t pc = 0;
            while(pc < m_code.size()){
                switch(m_code[pc]){
                    case OP_PRINT:{
                        //OP_PRINT <reg>
                        //print the value of the register
                        auto reg=m_code[pc+1];//reg is the register that has the required data
                        auto data=m_memory[reg];//data is the value of the register
                        std::cout << data << std::endl;
                        pc += 2;
                        break;
                    }
                    case OP_LOAD:{
                        //OP_LOAD <reg> <value>
                        //load the value into the register
                        auto reg=m_code[pc+1];//reg is the register where the data will be stored
                        auto data=m_code[pc+2];//data is the value that will be stored
                        m_memory[reg] = data;
                        pc += 3;
                        break;
                    }
                    case OP_MOV:{
                        //OP_MOV <reg1> <reg2>
                        //move the value of the register2 into the register1
                        auto reg1=m_code[pc+1];//reg1 is the register where the data will be stored
                        auto reg2=m_code[pc+2];//reg2 is the register that has the required data
                        m_memory[reg1] = m_memory[reg2];
                        pc += 3;
                        break;
                    }
                    default:
                        std::cout << "Unknown opcode: " << m_code[pc] << std::endl;
                        return;
                }
            }
        }
};


int main(){
    std::vector<int> code = {
        OP_LOAD , r1 , 1 ,//$r1 = 1
        OP_LOAD , r2 , 2 ,//$r2 = 2
        OP_MOV , r3 , r1 ,//$r3 = $r1
        OP_PRINT , r3 ,//print $r3
        OP_PRINT , r2 ,//print $r2
        OP_PRINT , r1 ,//print $r1
    };
    auto vm=VM(code);
    vm.run();
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Компиляция и запуск

Используйте следующую команду для компиляции clang++ file.cpp -o output . Затем запустите его ./output . Вы должны увидеть вывод на консоли

Заключение

Итак, как вы видите, виртуальные машины действительно просты в реализации. Я бы попросил вас реализовать свою собственную VM и расширить ее. Спасибо за чтение:)

Оцените статью
Procodings.ru
Добавить комментарий