0x1. 虚拟机原理
lua的虚拟机执行基于函数栈,所有的临时访问存储都基于栈,函数间的参数传递也是。在lua的官方说明里叫做寄存器。前面也提到过虚拟机的指令里面保存的是寄存器索引。
为了方便阐述栈虚拟机原理,这里以下面这段代码为例子来进行阐述,下面这段代码本身没有什么作用,只是为了方便阐述。这里有两个函数,分别为parent,clild
。parent
会调用chilld
,并把返回值放到psum
。这里之所以要有psum = psum + 1
是为了防止被lua优化成尾调用
。
function clild(num1, num2)
local csum = num1 + num2
return csum
end
function parent()
local p1 = 1
local p2 = 1
local psum = clild(p1, p2)
psum = psum + 1
end
--
编译结果:
函数clild
function <test.lua:1,4> (3 instructions, 12 bytes at 0x55ece9ff5a50)
2 params, 3 slots, 0 upvalues, 3 locals, 0 constants, 0 functions
1 [2] ADD 2 0 1
2 [3] RETURN 2 2
3 [4] RETURN 0 1
constants (0) for 0x55ece9ff5a50:
locals (3) for 0x55ece9ff5a50:
0 num1 1 3
1 num2 1 3
2 csum 2 3
upvalues (0) for 0x55ece9ff5a50:
函数parent
function <test.lua:6,11> (8 instructions, 32 bytes at 0x55ece9ff5f10)
0 params, 5 slots, 0 upvalues, 3 locals, 2 constants, 0 functions
1 [7] LOADK 0 -1 ; 1
2 [8] LOADK 1 -1 ; 1
3 [9] GETGLOBAL 2 -2 ; clild
4 [9] MOVE 3 0
5 [9] MOVE 4 1
6 [9] CALL 2 3 2
7 [10] ADD 2 2 -1 ; - 1
8 [11] RETURN 0 1
constants (2) for 0x55ece9ff5f10:
1 1
2 "clild"
locals (3) for 0x55ece9ff5f10:
0 p1 2 8
1 p2 3 8
2 psum 7 8
upvalues (0) for 0x55ece9ff5f10:
--
函数栈
lua栈或者称为lua的寄存器,是由TValue
组成的数组。TValue
是lua用于保存数据的数据结构。
下面这个过程是parent
调用clild
时栈的变化情况。
- 调用方将
Closure
,函数参数放到栈中 - 调用
Call
指令执行被调用函数 - 被调用函数执行完成后将返回值放到以
Closure
为起始地址的地方。如果有多个参数,则按顺序存放。
数据结构
lua中栈的回溯没有完全放到栈帧中,而是由专门的结构CallInfo
来保存。这个结构保存了栈帧恢复所需要的所有信息。而lua_state
中的base/top
始终指向当前正在执行的栈帧。