這裡要說的指令,並不是x86體系結構下的機器指令,而是虛擬機器指令。jerry虛擬機器的設計並不是非常複雜——甚至看起來很不靈活——它是乙個棧式虛擬機器,不包括任何暫存器(除了指令計數器之外),所有的指令都是基於棧進行的。在文章最後將給出完整的指令集。現在先給出指令資料結構,它們包括
typedef enum instructioncode;
// 依舊 cool 風格的成員
// segoffset 指令的偏移值
// code 指令碼
#define memberabstractinstruction \
int segoffset; \
instructioncode code; /*******/
// 指令資料結構基類
struct abstractinstruction ;
// 無引數的指令
struct noparaminstruction ;
// 帶整型引數的指令
struct intparaminstruction ;
// 帶實型引數的指令
struct realparaminstruction ;
// 跳轉指令
// targetins 目標指令指標
struct jumpinstruction ;
也許你會覺得奇怪,為什麼跳轉指令不計入整型引數的指令,要單獨拿出來,還弄個指標引數?如果這是個直譯器的話,問題還不大,但指標如果存放進位元組碼檔案再取回記憶體,如果運氣不是好到僱傭大猩猩能敲出jerry編譯程式的話,那麼指標指向的位址將不是我們期望的。因此,這裡的目標指令指標的含義將會在最後寫出指令時變化為整數偏移量。
不在這一步就算好所有指令的位址,然後用整數來儲存這個域,是考慮到後期優化。否則,某些刪去無用指令的優化,將會導致跳轉指令的偏移失效。為了避免每次這樣小動作都重新計算跳轉指令的偏移,方法就是,讓跳轉指令的偏移量在寫出指令到檔案之前的最後關口才確定下來。確定的方法就是用指令的 segoffset 域差值來計算——這樣一來 segoffset 的值也必須到那個時候(之前一點點)計算才行。
剛才提到了,虛擬機器是基於棧的,這就帶來了很多偷懶的機會,比如加法指令就不用去操心拿那幾個暫存器擺弄了。順帶提一下,jerry的指令大多數是定長的,只有一部分帶引數指令會將引數放在指令末尾,它們的長度較其他指令長。
運算指令
int_plus 彈出棧頂 b 和次棧頂 a,計算 a + b 壓入棧中
int_minus -
int_multiply *
int_divide /
int_lt <
int_le <=
int_eq ==
int_ne !=
int_gt >
int_ge >=
注意上面 a 是次棧頂,而b 是棧頂,下面也是。
int_assign 彈出棧頂 b 和次棧頂 a,將 b 的值寫入 a 指向的位址中去,然後在棧頂壓入 b 的值。
對於實型數,有一整套對應的指令:
real_plus, real_minus, real_multiply, real_divide, real_assign,
real_lt, real_le, real_eq, real_ne, real_gt, real_ge
io 指令
read_int 彈出棧頂 a,輸入乙個整數,將值放入 a 指向的位址中去。
read_real 實數
write_int 彈出棧頂整數 a 輸出。
write_real 實數
執行時型別轉換
int_2_real
彈出棧頂整數,轉換成實數壓回棧中
real_2_int 實數 整數
常數載入
load_int 彈出棧頂 a,輸入乙個整數,從 a 指向的位址載入乙個整數到棧頂
load_real 實數 彈棧
pop 其實就是單純地讓棧頂指標向基部靠攏,這是乙個帶整型引數的指令,引數說明了彈出的位元組數。
常數載入
const_int 載入乙個常數整數到棧頂,這是乙個帶整型引數的指令。
const_real 實數 實型
控制指令
jmp 無條件跳轉,引數為整數,表示偏移量
jmp_if_top 彈出棧頂整數 a,如果 a 不為 0,則跳轉,引數為整數,表示偏移量
jmp_not_top 為
end_program 終止程式
其他nop 無操作
結束。
typedef enum instructioncode;// 根據算術符號的列舉值獲取對應操作碼,注意兩個列舉的順序要一致
#define getcodebyintop(x) ((x) - plus + int_plus)
#define getcodebyrealop(x) ((x) - plus + real_plus)
// 是否為無引數操作
#define isnoparamins(x) (((x) >= int_plus && (x) <= load_real) \
|| nop == (x) || end_program == (x))
// 是否為整型引數操作
#define isintparamins(x) (pop == (x) || const_int == (x))
// 是否為實型引數操作
#define isrealparamins(x) (const_real == (x))
// 是否為跳轉
#define isjump(x) (jmp <= (x) && (x) <= jmp_not_top)
虛擬機器 指令寫出
是的,還有幾個節點的指令生成沒有測試呢,包括流控制語句,io.不過呢,流控制是相對比較容易優化的,進行 生成指令再對比 的測試不太好維護,而 io 指令很簡單 所以要不這樣,把指令寫出來,然後開個虛擬機器執行,如果執行沒問題就算測試通過了.一些常見的語義錯誤處理在之前的之前的內容中都以注釋形式出現,...
Bochs虛擬機器debug指令
在學習 linux核心完全注釋 時,為了方便,直接使用了趙博提供的linux 0.11 devel 040923.zip來debug,幫助理解 後來又在哈工大的 上發現了他們作業系統課程的實驗,同樣是關於linux 0.11版本的。後來在使用的過程中,我發現趙博使用的是bochs 2.1.1,而哈工...
虛擬機器指令 shell的使用
在linux學習者初步使用和學習linux的時候,會發現我們對這套作業系統是比較陌生的。而我們想要玩轉linux,那麼就不得不執行一些具有威脅性質的指令,可能會更改系統檔案,在此過程中,稍有不慎就可能會導致系統無法使用甚至癱瘓。因此保險起見,我們在最初的階段,將使用虛擬機器來幫助我們先去了解linu...