1.2.2 虚拟机指令的编码方式
前面介绍了虚拟机核心的数据结构和基本的运行方式,使读者对Lua虚拟机有了大概的认识。Lua虚拟机运行的本质,就是不断获取并运行Lua的虚拟机指令。在Lua中,虚拟机有自己能够识别的指令集,在这个集合里有不同的指令,会执行不同的功能。Lua脚本经过编译后,会生成包含在指令集里的指令,以确保虚拟机能够识别。在探讨Lua虚拟机指令集之前,先来了解一下Lua虚拟机指令的几种编码方式。
Lua虚拟机指令的编码方式(也是本书实现Lua解释器采用的编码方式)主要有3种,它们分别是iABC、iABx和iAsBx。虚拟机指令信息是保存在32bit无符号整型变量中,指令信息、参数信息会被编入这样的整型变量中。每个指令一共有32bit(位),并且所有的指令长度都是一样的。3种模式所表示的编码方式是不一样的。
iABC模式的编码方式如图1-16所示。从图中可以看到,该模式的指令主要由4个部分组成,分别是opcode域、A域、B域和C域。
·图1-16
opcode就是操作码,本质上指代的是虚拟机指令集里的指令(每个指令都有自己的数值编码),也就是说,只要识别它就能够知道当前用的是哪个指令了。opcode占6bit,因此它能够表示的最大值是26,也就是64,指令集的指令上限是64个。本书实现的Lua虚拟机,并没有包含官方所有的指令,随书工程实现的指令一共是43个。
紧临opcode域的是A域,它表示的是被操作的目标,本质就是代表寄存器的位置。前文也提到过,Lua虚拟机的寄存器实际上用的是栈上的空间,因此A域的值指代的就是栈上的某个位置,在这个指令里被当作寄存器使用了。A域占8bit,它能表示的最大值是256(所表示的值落在0~255这个区间),实际上限制了栈的有效空间(因为超过255,虚拟机指令就找不到这个位置了)。
接着是B域和C域,这两个域主要是存放参数信息。它们各占9bit,能表示的最大范围是29,也就是512。B域和C域具体代表什么信息,实际上是由opcode决定的。比如,操作对象如果是字符串,字符串本身的内容是没法编入这么短的域中的;因此,需要将不同的信息存放到不同的位置,再将它们的索引信息找出编入指令中。Lua的绝大多数指令,采用的是iABC模式。
iABx模式的编码方式如图1-17所示。和iABC模式不一样的是,iABx模式没有B和C域,而只有一个Bx域,这个域占18bit。使用这种模式的指令很少,只有OP_LOADK、OP_LOADKX和OP_CLOSURE三个指令。Bx所代表的值是无符号的。
·图1-17
iAsBx模式的编码方式如图1-18所示。iAsBx的模式和iABx大体相当,只是高18bit的值是有符号的,可以表示负数。但是sBx域并没有采取二进制补码的方式,而是采用一个bias值作为0值分界。因为sBx域占18bit,因此它能表示的最大无符号整数值是262143(218-1)。那么它的零值则是131071(262143>>1)。当需要它表示-1时,那么sBx域的值为131070(131071-1)。采用iAsBx模式的指令一般和跳转有关系,比如OP_JMP、OP_FORLOOP、OP_FORPREP和OP_TFORLOOP指令等。
·图1-18