在上次的編譯原理練習(xí)中,生成的目標(biāo)代碼是別人寫的一個基于寄存器的簡單虛擬機。這
回這個簡單的基于棧的虛擬機,純碎是為了彌補上回的練習(xí)不足。
基于寄存器(register based)的虛擬機和基于棧(stack based)的虛擬機主要的不同在于
對指令運算的中間值的保存方式。這些中間值包括各種運算的結(jié)果值,傳給各個指令的參
數(shù)等等。前者一般會設(shè)置幾個寄存器,如累加寄存器;后者則沒有寄存器,只有一個用來
保存這些值的棧。例如,這里我實現(xiàn)的SM(stack based machine)中的ADD指令:
ADD:從棧中依次彈出兩個數(shù)a和b,然后將b+a壓棧(b是左操作數(shù))。基于這樣一個方
式,SM中大部分指令都不需要操作數(shù),其操作數(shù)都直接從棧頂取。因為這個虛擬機僅僅是
用于上回設(shè)計的簡單語言的運行,即沒有函數(shù)、只有數(shù)字類型、有if和while。在這回練習(xí)中
我甚至把邏輯運算符給閹割了,只保留了大于小于之類的關(guān)系運算符。以下是該語言計算階
乘的例子:
read x;
if( x > 0 )
{
fac = 1;
while( x > 0 )
{
fac = fac * x;
x = x - 1;
}
write fac;
}
else
{
write 0;
}
基本上同《編譯原理與實踐》里的例子一樣,這樣省得我去琢磨語言文法。
不過,SM中還是有一個寄存器,即指令指針寄存器(pc),永遠(yuǎn)指向?qū)⒁獔?zhí)行的指令。在實現(xiàn)中,
所有指令都被保存一個數(shù)組里,所以pc就是一個指向數(shù)組索引的整數(shù)值。
SM中有一個簡單的內(nèi)存,只用于保存程序中的全局變量(只有全局變量)。同樣,這個虛擬的
內(nèi)存也被簡單地用一個數(shù)組來實現(xiàn),所以,指令中的所有地址,都是數(shù)組索引值。
SM的代碼文件直接就是指令序列的二進制表示。在這個二進制文件中,內(nèi)容依次為:操作碼(1
字節(jié)),操作數(shù)(4字節(jié),如果有的話),操作碼,操作數(shù),。。。SM讀取這樣的文件,將這些
指令放進指令數(shù)組,然后逐條解釋執(zhí)行,直到遇到空指令。
代碼中的test是上面簡單提到的編程語言的編譯程序,該程序?qū)⒃创a編譯為SM可執(zhí)行的二進制
文件(sm后綴)。為了方便調(diào)試,SM本身可以根據(jù)命令行參數(shù)輸出二進制文件對應(yīng)的反匯編代碼,
這可以方便我調(diào)試編譯程序的代碼生成是否正常工作。同時,當(dāng)初為了測試SM的正確性,還寫了
個簡單的匯編程序(sasm),可以把SM的匯編代碼轉(zhuǎn)換為二進制文件。
這回我特地在文法中間插入action丟給yacc處理,在賦值語句中一切正常。但是在if中yacc依然
提示警告,看起來應(yīng)該跟if中的懸掛else二義性有關(guān)系。不過通過添加空的文法符號,居然解決了。
不清楚為什么上回死活有問題,詭異了。
下載SM