Привет всем! В этом уроке я хочу показать вам, как создать свой собственный байткод 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 и расширить ее. Спасибо за чтение:)