Lua5.3 VM 分析(三)表达式运算
来源:互联网 发布:已备案域名已删除 编辑:程序博客网 时间:2024/06/09 15:19
Lua5.3 VM 分析(三)表达式运算
二元运算
+、-、*、%、^、/、//、&、 |、\~、\<\<、>> 这 12 种二元运算
OP_ADD、OP_SUB、OP_MUL、OP_DIV、OP_POW、OP_MOD、OP_IDIV、OP_BAND、OP_BOR、OP_BXOR、OP_SHL、OP_SHR
###
vmcase(OP_ADD) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Number nb; lua_Number nc; if (ttisinteger(rb) && ttisinteger(rc)) { lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); setivalue(ra, intop(+, ib, ic)); } else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { setfltvalue(ra, luai_numadd(L, nb, nc)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_ADD)); } vmbreak; }
这些操作类似,都是以两个对象作为操作对象,经过运算后,将结果放入寄存器 A 中。
对于这些数值类型之间的运算,做了优化,不会判断和出发元方法。这样可以提高效率。
1、首先取出 两个参数的值 rb 与 rc ,利用 ttisinteger 宏判断是不是 lua_Integer 小类型,如果是则利用 ivalue 宏 转换成 lua_Integer 类型的值。
TValue *io=ra; val_(ra).i=(ib+ic); settt_(io, LUA_TNUMINT);
将 ib 与 ic 做数值运算后的结果赋值给 ra.i (lua_Integer 类型 Value 结构中定义)
最后对ra->tt_ 设置类型标志。
2、如果不是 LUA_TNUMINT 子类型 则可能是 LUA_TNUMFLT 子类型
(float),利用tonumber 宏将 rb 与 rc 转换后存储在 lua_Number nb;
lua_Number nc; 最后
TValue *io=(ra); val_(io).n=(nb+nc); settt_(io, LUA_TNUMFLT);
3、其他情况 触发 元方法,有点复杂。用Call_binTM 函数做了一些封装。
函数逻辑: 先判断第一个对象是否有需要的元方法,如果找不到则去第二个函数
上面查找元方法。 如果两个对象上都没有找打元方法则返回 到 luaT_trybinTM
函数中,除了TM_CONCAT、TM_BAND、TM_BOR、TM_BXOR、TM_SHL、
TM_SHR、TM_BNOT 特殊处理外 ,利用 luaG_opinterror 抛出异常。
vmcase(OP_ADD) { TValue *rb = RKB(i); TValue *rc = RKC(i); lua_Number nb; lua_Number nc; if (ttisinteger(rb) && ttisinteger(rc)) { lua_Integer ib = ivalue(rb); lua_Integer ic = ivalue(rc); setivalue(ra, intop(+, ib, ic)); } else if (tonumber(rb, &nb) && tonumber(rc, &nc)) { setfltvalue(ra, luai_numadd(L, nb, nc)); } else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_ADD)); } vmbreak; }int luaT_callbinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event) { const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ if (ttisnil(tm)) tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ if (ttisnil(tm)) return 0; luaT_callTM(L, tm, p1, p2, res, 1); return 1;}void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2, StkId res, TMS event) { if (!luaT_callbinTM(L, p1, p2, res, event)) { switch (event) { case TM_CONCAT: luaG_concaterror(L, p1, p2); case TM_BAND: case TM_BOR: case TM_BXOR: case TM_SHL: case TM_SHR: case TM_BNOT: { lua_Number dummy; if (tonumber(p1, &dummy) && tonumber(p2, &dummy)) luaG_tointerror(L, p1, p2); else luaG_opinterror(L, p1, p2, "perform bitwise operation on"); /* else go through */ } default: luaG_opinterror(L, p1, p2, "perform arithmetic on"); } }}
其中 OP_ADD、OP_SUB、OP_MUL、OP_MOD、OP_IDIV 总是处理 int 、float、元方法 三种情况。
OP_DIV 、OP_POW 总是处理 float 类型 与 元方法 两种情况。
OP_BAND、OP_BOR、OP_BXOR、OP_SHL、OP_SHR 这几种opcode 只处理 int 与 元方法 两种情况。
一元运算
-、\~、not、length of R(B) 这 4 种一元运算
OP_UNM、OP_BNOT、OP_NOT、OP_LEN
OP_UNM
总是处理 int 、float、元方法 三种情况。
vmcase(OP_UNM) { TValue *rb = RB(i); lua_Number nb; if (ttisinteger(rb)) { lua_Integer ib = ivalue(rb); setivalue(ra, intop(-, 0, ib)); } else if (tonumber(rb, &nb)) { setfltvalue(ra, luai_numunm(L, nb)); } else { Protect(luaT_trybinTM(L, rb, rb, ra, TM_UNM)); } vmbreak; }
OP_BNOT
总是处理 int 、元方法 两种情况。
vmcase(OP_BNOT) {TValue *rb = RB(i);lua_Integer ib;if (tointeger(rb, &ib)) { setivalue(ra, intop(^, ~l_castS2U(0), ib));}else { Protect(luaT_trybinTM(L, rb, rb, ra, TM_BNOT));}vmbreak; }
OP_NOT
对 rb 取负
vmcase(OP_NOT) {TValue *rb = RB(i);int res = l_isfalse(rb); /* next assignment may change this value */setbvalue(ra, res);vmbreak; }
OP_LEN
Len 操作用于取对象长度, 根据 Lua 的定义:
1. 对于字符串取长度;
2. 对于表则取数组部分长度;
3. 其他情况调用 元方法 len。
void luaV\_objlen (lua\_State *L, StkId ra, const TValue *rb) { const TValue \*tm; switch (ttnov(rb)) { case LUA_TTABLE: { Table *h = hvalue(rb); tm = fasttm(L, h->metatable, TM_LEN); if (tm) break; /* metamethod? break switch to call it */ setivalue(ra, luaH_getn(h)); /* else primitive len */ return; } case LUA_TSTRING: { setivalue(ra, tsvalue(rb)->len); return; } default: { /* try metamethod */ tm = luaT_gettmbyobj(L, rb, TM_LEN); if (ttisnil(tm)) /* no metamethod? */ luaG_typeerror(L, rb, "get length of"); break; } } luaT\_callTM(L, tm, rb, rb, ra, 1); }
OP_CONCAT 字符串链接
将R(B) 到 R(C) 之间的所有值,都以字符串方式连接起来,把结果放到R(A) 中。这个连接过程是通过 luaV_concat 函数完成的。
函数逻辑:
1. 通过临时修改栈顶地址为 C ,然后连接 R(B) 到 R(C) 的值,将结果临时存在 R(B) 中。
2. 再将R(B) 复制到 R(A),最后将栈顶位置调整回去。
3. R(B)到R(C) 以及之后的寄存器,不能被后续指令读取。也就是说 R(B) 与 R(C) 寄存器必须在栈顶工作。
vmcase(OP\_CONCAT) { int b = GETARG\_B(i); int c = GETARG\_C(i); StkId rb; L->top = base + c + 1; /\* mark the end of concat operands \*/ Protect(luaV\_concat(L, c - b + 1)); ra = RA(i); /\* 'luav\_concat' may invoke TMs and move the stack \*/ rb = b + base; setobjs2s(L, ra, rb); checkGC(L, (ra >= rb ? ra + 1 : rb)); L->top = ci->top; /\* restore top \*/ vmbreak; }
由于字符串连接操作,可能会触发元方法,导致数据栈空间扩展。所以必须在luaV_concat 函数调用完后 重新获取 ra = RA(i) (因为ra 不再指向原来的位置)。
在OP_CONCAT 操作的最后,重置了数据栈的栈顶。
Lua字节码以寄存器的方法来理解数据栈空间,在大多数情况下,用到多少寄存器是在编译期生成字节码的时候决定的。所以在函数原型Proto 结构里有 maxstacksize 这个信息,同时在运行时,会把这段空间的top 记录在 CallInfo->top 中。 Lua VM 在运行时 会以堆栈的方法利用这个数据栈,这种栈形式利用数据堆栈都是临时行为,使用完毕后应该重置数据栈栈顶。
void luaV_concat (lua_State *L, int total) { lua_assert(total >= 2); do { StkId top = L->top; int n = 2; /* number of elements handled in this pass (at least 2) */ if (!(ttisstring(top-2) || cvt2str(top-2)) || !tostring(L, top-1)) luaT_trybinTM(L, top-2, top-1, top-2, TM_CONCAT); else if (tsvalue(top-1)->len == 0) /* second operand is empty? */ cast_void(tostring(L, top - 2)); /* result is first operand */ else if (ttisstring(top-2) && tsvalue(top-2)->len == 0) { setobjs2s(L, top - 2, top - 1); /* result is second op. */ } else { /* at least two non-empty string values; get as many as possible */ size_t tl = tsvalue(top-1)->len; char *buffer; int i; /* collect total length */ for (i = 1; i < total && tostring(L, top-i-1); i++) { size_t l = tsvalue(top-i-1)->len; if (l >= (MAX_SIZE/sizeof(char)) - tl) luaG_runerror(L, "string length overflow"); tl += l; } buffer = luaZ_openspace(L, &G(L)->buff, tl); tl = 0; n = i; do { /* copy all strings to buffer */ size_t l = tsvalue(top-i)->len; memcpy(buffer+tl, svalue(top-i), l * sizeof(char)); tl += l; } while (--i > 0); setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); /* create result */ } total -= n-1; /* got 'n' strings to create 1 new */ L->top -= n-1; /* popped 'n' strings and pushed one */ } while (total > 1); /* repeat until only 1 result left */}
- Lua5.3 VM 分析(三)表达式运算
- Lua5.3 虚拟机指令分析(三)表达式运算
- Lua5.3 VM 分析(一)字节码运行
- Lua5.3 VM 分析(二)表处理
- Lua5.3 VM 分析(四)分支和跳转
- Lua5.3 VM 分析(七)生成闭包
- Lua5.3 VM 分析(八)For 循环
- Lua5.3 虚拟机指令分析(一)概述
- Lua5.3 虚拟机指令分析(二)赋值指令
- Lua5.3 虚拟机指令分析(四)分支与跳转
- Lua5.3 虚拟机指令分析(五)函数调用
- Lua5.3 虚拟机指令分析(六)不定参数
- Lua5.3 虚拟机指令分析(八)循环
- Lua5.3 虚拟机指令分析(十)表相关指令
- 三、运算符 表达式
- 三元运算表达式分析
- 探索Lua5.2内部实现:编译系统(3) 表达式
- Lua5.1代码阅读(三):lcode.h/lcode.c
- RCNN, Fast-RCNN, Faster-RCNN
- 《算法导论》第五章-第4节_练习(参考答案)
- 打扫房间的各种方法 —— Java虚拟机的垃圾收集算法清单
- 1037. 在霍格沃茨找零钱(20)
- 如何更好的做计划-SMART原则
- Lua5.3 VM 分析(三)表达式运算
- 【数学基础】【欧拉函数解析模板】【欧拉筛法实现求1~n】【求单个n】
- Lua5.3 VM 分析(四)分支和跳转
- Lua5.3 VM 分析(七)生成闭包
- Lua5.3 VM 分析(八)For 循环
- linearlayout之margin和peddling
- talk is expensive , Don't show me your code at that time
- Linux 下如何查看cpu信息
- swift3自定义UIBotton(带动画效果)