Lua5.3 VM 分析(一)字節碼運行

Lua5.3 VM 分析(一)字節碼運行

luaV_execute 是Lua VM 執行一段字節碼的入口。Lua VM 就是一個狀態機,從當前調用棧上次運行點開始解釋字節碼指令,直到下一個 C 邊界跳出點(可以是函數執行完畢,也可以是一次協程 yield 操作)。

Lua 函數部分

typedef struct CallInfo {
  StkId func;  /* function index in the stack */
  StkId top;  /* top for this function */
  struct CallInfo *previous, *next;  /* dynamic call link */
  union {
    struct {  /* only for Lua functions */
      StkId base;  /* base for this function */
      const Instruction *savedpc;
    } l;
    struct {  /* only for C functions */
      lua_KFunction k;  /* continuation in case of yields */
      ptrdiff_t old_errfunc;
      lua_KContext ctx;  /* context info. in case of yields */
    } c;
  } u;
  ptrdiff_t extra;
  short nresults;  /* expected number of results from this function */
  lu_byte callstatus;
} CallInfo;

savepc域 保存着指向當前指令的指針;base 域保存着當前函數的數據棧棧底指針。
每一次進入或退出一層Lua 函數,luaV_execute 並不會產生一次 C 層面的函數調用。也就是說,從Lua 函數中調用另一個Lua 函數,並不會產生一次獨立的luaV_execute 調用。

luaV_execute

函數實現如下圖
這裏寫圖片描述

Lua 自己維護數據棧和調用棧,在解析字節碼的時候,用 goto 語句來更新棧信息。 在luaV_execute 中定義了 newframe 這個跳轉標籤,Lua 函數執行 OP_CALL 、OP_TAILCALL 、OP_RETURN 指令都會跳轉到這個標籤,更新棧幀繼續運行。
1. ci 變量從 當前 L 從獲取當前正在調用的函數指針。
2. cl 變量是一個Lua 閉包類型,放置調用棧中當前函數對象,從ci->func中獲取。
3. k 變量是 TValue 類型,放置當前函數的常量表, 從cl->p->k中獲取。
4. base 變量是 StkId類型,放置當前數據棧 棧底的位置,從cl->u.l.base獲取。
5. luaV_execute 解釋字節碼的過程也就是利用一個死循環,依次解析字節碼指令,當前 指令 i 從 ci->u.l.savepc 中獲取。

所有的指令都會操作寄存器 A ,從 Lua VM 的角度看,寄存器就是數據棧上的變量,所以可以將寄存器 A 所指變量預先取出放到局部變量ra 中。

ra = RA(i)。某些指令操作在 vm 運行過程中會改變數據棧的大小(伸縮),而 ra 是一個指向數據棧的指針,而不是一個索引。這種情況下,一旦數據棧發生變化,就需要重新獲取ra 的值。

同理, base 變量 是一個指向數據棧棧底的指針,也會因爲某些指令操作發生變化。這個時候就需要重新對 base 重新賦值。 棧底 base 作爲基址是一個參考量,一直需要使用,所以重置 base 的值很頻繁, Lua 提供了一個 Protect 宏,將重置base的操作包裹起來。

define Protect(x)   { {x;}; base = ci->u.l.base; }

在死循環中,利用 C 語言的 switch /case 語句 針對每種OpCode 提供不同的操作。
define vmdispatch(o) switch(o)
define vmcase(l) case l:
define vmbreak break

發佈了198 篇原創文章 · 獲贊 36 · 訪問量 27萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章