虚拟机总共有两种,基于栈的和基于寄存器的。栈式的生成字节码会更多,但是单个指令比较简单,虚拟机更好设计。基于寄存器方式实现的虚拟机以后更好优化,执行效率会更高。当然硬件层面的寄存器和内存完全是不同的概念。而软件层面的虚拟寄存器
就是数据段的内存
。比如基于寄存器的虚拟机都是用一个变量(内存)来模拟寄存器。
可以参考一下CPU的相关实现,做一个指令的子集就行。
如果只是做赋值、循环、函数调用的话,我们还需要引入加减法,判断,跳转功能。
然后我们管理两段空间,分别是指令空间
、数据空间
就好了。
指令采用一个简单方式:1个字节的指令编码
,1个标志判断
,2个2字节参数
。
1 | typedef struct inst |
接下来我们需要有一个保存状态的结构
1 | typedef struct vm_state |
定义一下需要的指令:
1 | #define IADD 1 // 加法 |
定义一下执行指令的条件,也就是说在什么条件下才执行该指令
1 | #define FNA 0 // 任何状态下都执行 |
好了,然后我们可以写一个虚拟机的执行的函数了
1 | void execute(vm_state_t *state) |
这虚拟机就算写完了。
这个虚拟机能做点什么呢?比如做一个类似这样的加法还是可以的:
1 | int sum = 0; |
我们可以这样处理:
保存sum在数据段0号
保存i在数据段1号
保存101立即数在数据段2号
保存立即数1在数据段3号
翻译的指令如下:
1 | 0000 ILD 2, 101 // 放立即数101到2号位 |
要想测试一下,写一个函数把这些指令放进去就行。
1 | #include <stdio.h> |
这个简单的虚拟机是否可以实现函数调用呢?答案是可以的,利用ISTIP、ILDIP就可以了,返回地址可以保存在数据段中。
这个虚拟机非常简单,效率低、空间浪费、功能有限,不过在这个基础上可以自己优化、扩充嘛。
关于需要阅读什么书籍?这个要看自己当前的水平了,直接介绍专业著作怕是不一定能看的下去。我建议可以自己先写着玩玩,实现一些功能,尝试编译一些自定义的脚本代码到自己的虚拟机上,然后再阅读一点编译原理、垃圾回收、JIT相关的文章,看看Intel、ARM的CPU的指令手册,把自己实验性质的虚拟机做的像样一些。玩的差不多了,可以看看
https://jilp.org/vol5/v5paper12.pdf
最后:上述代码回答时随手写的,没有编译调试过,难免有很多笔误…