Lua1.1 虛擬機指令分析(一)

在語法分析 lua_parse 以後,調用 lua_execute 來執行語法分析生成的字節碼。
虛擬機的指令是一個枚舉型,就是在 opcode.h 中的 OpCode, 經過 lua_execute 中的那個 switch case 來看下指令對應的操做。
> PUSHNIL
c++

   case PUSHNIL: tag(top++) = T_NIL; break;

  設置棧頂的 Object 類型爲 T_NIL,這個棧就是以前所說的那個 Lua 和 C 之間交互的那個棧。

> PUSH0, PUSH1, PUSH2
express

   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 方法
lua

   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
code

   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。(未完待續)

相關文章
相關標籤/搜索