0x1. 虚拟机原理


lua的虚拟机执行基于函数栈,所有的临时访问存储都基于栈,函数间的参数传递也是。在lua的官方说明里叫做寄存器。前面也提到过虚拟机的指令里面保存的是寄存器索引。

为了方便阐述栈虚拟机原理,这里以下面这段代码为例子来进行阐述,下面这段代码本身没有什么作用,只是为了方便阐述。这里有两个函数,分别为parent,clildparent会调用chilld,并把返回值放到psum。这里之所以要有psum = psum + 1是为了防止被lua优化成尾调用

  1. function clild(num1, num2)
  2. local csum = num1 + num2
  3. return csum
  4. end
  5. function parent()
  6. local p1 = 1
  7. local p2 = 1
  8. local psum = clild(p1, p2)
  9. psum = psum + 1
  10. end
  11. --
  12. 编译结果:
  13. 函数clild
  14. function <test.lua:1,4> (3 instructions, 12 bytes at 0x55ece9ff5a50)
  15. 2 params, 3 slots, 0 upvalues, 3 locals, 0 constants, 0 functions
  16. 1 [2] ADD 2 0 1
  17. 2 [3] RETURN 2 2
  18. 3 [4] RETURN 0 1
  19. constants (0) for 0x55ece9ff5a50:
  20. locals (3) for 0x55ece9ff5a50:
  21. 0 num1 1 3
  22. 1 num2 1 3
  23. 2 csum 2 3
  24. upvalues (0) for 0x55ece9ff5a50:
  25. 函数parent
  26. function <test.lua:6,11> (8 instructions, 32 bytes at 0x55ece9ff5f10)
  27. 0 params, 5 slots, 0 upvalues, 3 locals, 2 constants, 0 functions
  28. 1 [7] LOADK 0 -1 ; 1
  29. 2 [8] LOADK 1 -1 ; 1
  30. 3 [9] GETGLOBAL 2 -2 ; clild
  31. 4 [9] MOVE 3 0
  32. 5 [9] MOVE 4 1
  33. 6 [9] CALL 2 3 2
  34. 7 [10] ADD 2 2 -1 ; - 1
  35. 8 [11] RETURN 0 1
  36. constants (2) for 0x55ece9ff5f10:
  37. 1 1
  38. 2 "clild"
  39. locals (3) for 0x55ece9ff5f10:
  40. 0 p1 2 8
  41. 1 p2 3 8
  42. 2 psum 7 8
  43. upvalues (0) for 0x55ece9ff5f10:
  44. --

函数栈

lua栈或者称为lua的寄存器,是由TValue组成的数组。TValue是lua用于保存数据的数据结构。
image.png
下面这个过程是parent调用clild栈的变化情况。

  • 调用方将Closure,函数参数放到栈中
  • 调用Call指令执行被调用函数
  • 被调用函数执行完成后将返回值放到以Closure为起始地址的地方。如果有多个参数,则按顺序存放。

image.png

数据结构

lua中栈的回溯没有完全放到栈帧中,而是由专门的结构CallInfo来保存。这个结构保存了栈帧恢复所需要的所有信息。而lua_state中的base/top始终指向当前正在执行的栈帧。
image.png
image.png