您的位置:首页 > 编程语言 > Lua

Lua1.1 虚拟机指令分析

2016-09-22 17:55 232 查看
在语法分析 lua_parse 之后,调用 lua_execute 来执行语法分析生成的字节码。
虚拟机的指令是一个枚举型,就是在 opcode.h 中的 OpCode, 通过 lua_execute 中的那个 switch case 来看下指令对应的操作。
> PUSHNIL
case PUSHNIL: tag(top++) = T_NIL; break;
设置栈顶的 Object 类型为 T_NIL,这个栈就是之前所说的那个 Lua 和 C 之间交互的那个栈。

> PUSH0, PUSH1, PUSH2
case PUSH0: tag(top) = T_NUMBER; nvalue(top++) = 0; break;
case PUSH1: tag(top) = T_NUMBER; nvalue(top++) = 1; break;
case PUSH2: tag(top) = T_NUMBER; nvalue(top++) = 2; break;
设置栈顶的 Object 类型为 T_NUMBER,值为 0/1/2。 这个是指令优化,把操作数放到指令里,从而让指令更短,执行的更快些。
这几个指令是 PUSHBYTE 指令的优化版,或者叫特化版更合适一些。

> PUSHBYTE
case PUSHBYTE: tag(top) = T_NUMBER; nvalue(top++) = *pc++; break;
设置栈顶的 Object 类型为 T_NUMBER,值从当前字节码处(也就是 pc 指针处的一个字节)取得。值在字节码中占一个字节。

> PUSHWORD
case PUSHWORD:   {
CodeWord code;
get_word(code,pc);
tag(top) = T_NUMBER; nvalue(top++) = code.w;
}
break;
设置栈顶的 Object 类型为 T_NUMBER,值从当前字节码处(也就是 pc 指针处的两个字节)取得。值在字节码中占两个字节。
get_word 是 y.tab.c 中的 code_word 的相反过程,CodeWord 是个联合体
typedef union   {
struct {char c1; char c2;} m;
Word w;
} CodeWord;
在 code_word 方法
static void code_word (Word n)   {
CodeWord code;
code.w = n;
code_byte(code.m.c1);
code_byte(code.m.c2);
}
可以看到,设置的时候把值设置给 w, 之后调用 code_byte 分别对 w 的两个字节生成字节码。
由于 CodeWord 是个联合体,这里的 w 和 m 是同一片内存,所以 code_word 可以按预期正确执行。
这样的联合体操作是 C 语言里的一个小技巧,比如测字节序(例如你的处理器体系结构是大端序还是小端序)的时候就可以用这样的技巧。

> PUSHFLOAT
case PUSHFLOAT:
{
CodeFloat code;
get_float(code,pc);
tag(top) = T_NUMBER; nvalue(top++) = code.f;
}
break;
设置栈顶的 Object 类型为 T_NUMBER,值从当前字节码处(也就是 pc 指针处的四个字节)取得。值在字节码中占四个字节。
get_float 和上面的 get_word 情况一样,不再重复。

> PUSHSTRING
case PUSHSTRING:   {
CodeWord code;
get_word(code,pc);
tag(top) = T_STRING; svalue(top++) = lua_constant[code.w];
}
break;
设置栈顶的 Object 类型为 T_STRING,值从常量表从取得,下标从当前字节码处取得。下标占两个字节。

> PUSHLOCAL0, PUSHLOCAL1, PUSHLOCAL2, PUSHLOCAL3, PUSHLOCAL4,
PUSHLOCAL5, PUSHLOCAL6, PUSHLOCAL7, PUSHLOCAL8, PUSHLOCAL9,
case PUSHLOCAL0: case PUSHLOCAL1: case PUSHLOCAL2:   case PUSHLOCAL3: case PUSHLOCAL4: case PUSHLOCAL5:   case PUSHLOCAL6: case PUSHLOCAL7: case PUSHLOCAL8:   case PUSHLOCAL9: *top++ = *(base + (int)(opcode-PUSHLOCAL0)); break;
设置栈顶的 Object 为局部变量 N (0<=N<=9),这个也是指令优化。把当前指令 opcode 减去 PUSHLOCAL0 取得偏移量 N。
局部变量是从栈的 base 算起的偏移量,也可以理解为数组的下标。
这几个指令是 PUSHLOCAL 的特化版。

> PUSHLOCAL
case PUSHLOCAL: *top++ = *(base + (*pc++)); break;
设置栈顶的 Object 为局部变量 N (N 的偏移量从字节码中取得)。

> PUSHGLOBAL,
case PUSHGLOBAL:   {
CodeWord code;
get_word(code,pc);
*top++ = s_object(code.w);
}
break;
设置栈顶的 Object 为全局变量 N (N 从全局符号表中取得,它的下标从字节码中取得,占两个字节)。

> PUSHINDEXED,
case PUSHINDEXED:
--top;
if (tag(top-1) != T_ARRAY)
{
lua_reportbug ("indexed expression not a table");
return 1;
}
{
Object *h = lua_hashdefine (avalue(top-1), top);
if (h == NULL) return 1;
*(top-1) = *h;
}
break;
设置数组元素索引,当前的栈的 top 处为要设置的元素索引,top-1 为数组。
通过 lua_hashdefine 把 top 做为索引设置进数组,返回其所在键值对儿 Node 值 val 地址。
设置到 top-1 处,以备后续使用。

> PUSHMARK
case PUSHMARK: tag(top++) = T_MARK; break;
设置栈顶的 Object 为 T_MARK,这个用于标记,比如在函数调用时会在函数入栈后再入栈一个 T_MARK。

> PUSHOBJECT
case PUSHOBJECT: *top = *(top-3); top++; break;
设置栈顶的 Object 为栈顶的倒数第 4 个 Object。

> STORELOCAL0, STORELOCAL1, STORELOCAL2, STORELOCAL3, STORELOCAL4,
STORELOCAL5, STORELOCAL6, STORELOCAL7, STORELOCAL8, STORELOCAL9,
case STORELOCAL0: case STORELOCAL1: case STORELOCAL2:   case STORELOCAL3: case STORELOCAL4: case STORELOCAL5:   case STORELOCAL6: case STORELOCAL7: case STORELOCAL8:   case STORELOCAL9: *(base + (int)(opcode-STORELOCAL0)) = *(--top); break;
设置局部变量 N (0<=N<=9)为栈顶的 Object,这个也是指令优化。把当前指令 opcode 减去 STORELOCAL0 取得偏移量 N。
局部变量是从栈的 base 算起的偏移量,也可以理解为数组的下标。
这几个指令是 STORELOCAL 的特化版。

> STORELOCAL
case STORELOCAL: *(base + (*pc++)) = *(--top); break;
设置局部变量 N (N 的偏移量从字节码中取得)为栈顶的 Object。

> STOREGLOBAL
case STOREGLOBAL:   {
CodeWord code;
get_word(code,pc);
s_object(code.w) = *(--top);
}
break;
设置全局变量 N (N 从全局符号表中取得,它的下标从字节码中取得,占两个字节)为栈顶的 Object。

> STOREINDEXED0
case STOREINDEXED0:
if (tag(top-3) != T_ARRAY)
{
lua_reportbug ("indexed expression not a table");
return 1;
}
{
Object *h = lua_hashdefine (avalue(top-3), top-2);
if (h == NULL) return 1;
*h = *(top-1);
}
top -= 3;
break;
设置数组元素,数组位于 top-3, 数组索引位于 top-2, 数组值位于 top-1。
设置完成后,这三个 Object 出栈。

> STOREINDEXED
case STOREINDEXED:
{
int n = *pc++;
if (tag(top-3-n) != T_ARRAY)
{
lua_reportbug ("indexed expression not a table");
return 1;
}
{
Object *h = lua_hashdefine (avalue(top-3-n), top-2-n);
if (h == NULL) return 1;
*h = *(top-1);
}
top--;
}
break;
设置指定偏移量的数组元素,偏移量从字节码中取得,数组位于 top-3-n,索引位于 top-2-n,值位于栈顶。
设置完成后,栈顶值出栈。

> STORELIST0,
STORELIST
case STORELIST0:   case STORELIST:   {
int m, n;
Object *arr;
if (opcode == STORELIST0) m = 0;
else m = *(pc++) * FIELDS_PER_FLUSH;
n = *(pc++);
arr = top-n-1;
if (tag(arr) != T_ARRAY)
{
lua_reportbug ("internal error - table expected");
return 1;
}
while (n)
{
tag(top) = T_NUMBER; nvalue(top) = n+m;
*(lua_hashdefine (avalue(arr), top)) = *(top-1);
top--;
n--;
}
}
break;
设置数组值,m 为下标,n 为数组元素个数。要设置的数组值都位于栈上,栈顶为最后一个元素。
所以在 while 循环里给数组赋值是先给下标大的赋值,再给小的赋值,每赋值一个就出栈一个。

> STORERECORD
case STORERECORD:   {
int n = *(pc++);
Object *arr = top-n-1;
if (tag(arr) != T_ARRAY)
{
lua_reportbug ("internal error - table expected");
return 1;
}
while (n)
{
CodeWord code;
get_word(code,pc);
tag(top) = T_STRING; svalue(top) = lua_constant[code.w];
*(lua_hashdefine (avalue(arr), top)) = *(top-1);
top--;
n--;
}
}
break;
给记录赋值,n 为要赋值的个数。被赋值的元素索引从字节码中取得,右值从栈上取得,赋值后出栈。
记录是指下标为常量字符串的表,数组是指下标为整数的表。具体的区别请参见手册。

> ADJUST
case ADJUST:
{
Object *newtop = base + *(pc++);
while (top < newtop) tag(top++) = T_NIL;
top = newtop; /* top could be bigger than newtop */   }
break;
调整栈元素个数,元素个数从字节码取出。如果新的栈顶高于当前栈顶,当前栈顶到新的栈顶之间的元素赋空。
一般函数调用之后会调用它调整栈。 ADJUST 的下一个字节码(pc)是需要返回的函数返回值个数。
while 补空的意义就是如果需要返回的函数个数大于实际返回的,则补空。
(反之,需要的返回值少于实际返回的个数的话,多余的会被丢弃,这是通过设置 top 实现的。)

> CREATEARRAY
case CREATEARRAY:
if (tag(top-1) == T_NIL)
nvalue(top-1) = 101;
else    {
if (tonumber(top-1)) return 1;
if (nvalue(top-1) <= 0) nvalue(top-1) = 101;
}
avalue(top-1) = lua_createarray(nvalue(top-1));
if (avalue(top-1) == NULL)
return 1;
tag(top-1) = T_ARRAY;
break;
新建数组,元素个数从栈顶取得,如果没有指定或者指定一个负数,把它修正为 101。如果栈顶不是个数字,出错。
新建数组,赋值给栈顶元素,并设置栈顶 Object 类型为 T_ARRAY。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息