Lua源代码阅读(十)->Lua虚拟机指令格式

Lua虚拟机的指令格式如下图所示:

OpCode

我们来对上图做一些解释和分析.

首先能看到的时,Lua的指令是32位的,由低位到高位来进行解释,首先解析最低6位的OpCode,再根据OpCode获取该指令的具体格式,因此Lua最多支持2^6=64个指令.Lua代码中,将每个OpCode及其对应的指令格式都在lopcodes.h中的OpCode枚举类型中定义:

(lopcodes.h)
150 typedef enum {
151 /*----------------------------------------------------------------------
152 name    args  description
153 ------------------------------------------------------------------------*/
154 OP_MOVE,/*  A B R(A) := R(B)          */
155 OP_LOADK,/* A Bx  R(A) := Kst(Bx)         */
156 OP_LOADBOOL,/*  A B C R(A) := (Bool)B; if (C) pc++      */
157 OP_LOADNIL,/* A B R(A) := ... := R(B) := nil      */
158 OP_GETUPVAL,/*  A B R(A) := UpValue[B]        */
159
/* ... */ 
208 } OpCode;

每个OpCode右边紧随的注释说明了该OpCode的具体格式.

可是仅有注释里面写明的每个OpCode的格式还不够,因为这起不到程序上的约束和说明作用,于是Lua代码中使用了一个数组定义所有OpCode的一些说明信息:

(lopcodes.c)
59 #define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))
60 
61 const lu_byte luaP_opmodes[NUM_OPCODES] = {
62 /*       T  A    B       C     mode        opcode   */
63   opmode(0, 1, OpArgR, OpArgN, iABC)        /* OP_MOVE */
64  ,opmode(0, 1, OpArgK, OpArgN, iABx)        /* OP_LOADK */

这里用一个宏opmode封装了每个OpCode的具体格式,其中:

  • T:表示这是不是一条逻辑测试相关的指令,这种指令可能会将PC指针自增1.
  • A:表示这个指令会不会赋值给R(A).
  • B/C:B/C参数的格式
  • mode:这个OpCode的格式.

B/C参数的格式如下:

(lopcodes.h)
245 enum OpArgMask {
246   OpArgN,  /* argument is not used */       
247   OpArgU,  /* argument is used */
248   OpArgR,  /* argument is a register or a jump offset */
249   OpArgK   /* argument is a constant or register/constant */
250 };

OpArgN表示这个参数没有被使用,但是这里的意思并不是真的没有被使用,只是没有作为R()或者RK()宏的参数使用,

从图中可以看出,Lua共有三种指令格式:iABC,iABx,iAsBx.

Lua代码中提供了根据指令中的值得到相应数据的几个宏,

(lvm.c)
343 #define RA(i)   (base+GETARG_A(i))
344 /* to be used after possible stack reallocation */
345 #define RB(i)   check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))
346 #define RC(i)   check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i))
347 #define RKB(i)  check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \
348     ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i))
349 #define RKC(i)  check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \
350     ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i))
351 #define KBx(i)  check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i))

RA/RB/RC自不必多做解释,前面讲解Lua指令执行的时候已经说过,其含义就是以参数为偏移量在函数栈中取数据.

RKB/RKC的意思有两层,第一是这个指令格式只可能作用在opcode的B/C参数上,不会作用在参数A上,第二层意思是这个数据除了从函数栈中获取之外,还有可能从常量数组(也就是K数组)中获取,关键在于宏ISK的判断:

(lopcodes.h)
38  #define SIZE_B      9
118 /* this bit 1 means constant (0 means register) */
119 #define BITRK       (1 << (SIZE_B - 1))
120 
121 /* test whether value is a constant */
122 #define ISK(x)      ((x) & BITRK)

结合起来看,这个宏的含义就很简单了:判断这个数据的第八位是不是1,如果是则认为应该从K数组中获取数据,否则就是从函数栈中获取数据.

宏KBx也是自解释的,它不会从函数栈中取数据了,直接从K数组中获取数据.

你可能感兴趣的:(Lua源代码阅读(十)->Lua虚拟机指令格式)