三地址码简介
三地址碼簡(jiǎn)介
三地址碼(Three Address Code)是一種最常用的中間語(yǔ)言,編譯器可以通過(guò)它來(lái)改進(jìn)代碼轉(zhuǎn)換效率。每個(gè)三地址碼指令,都可以被分解為一個(gè)四元組(4-tuple)的形式:(運(yùn)算符,操作數(shù)1,操作數(shù)2,結(jié)果)。由于每個(gè)陳述都包含了三個(gè)變量,即每條指令最多有三個(gè)操作數(shù),所以它被稱為三地址碼。
編譯器
編譯器(compiler),是一種計(jì)算機(jī)程序,它會(huì)將用某種編程語(yǔ)言寫(xiě)成的源代碼(原始語(yǔ)言),轉(zhuǎn)換成另一種編程語(yǔ)言(目標(biāo)語(yǔ)言)。
它主要的目的是將便于人編寫(xiě)、閱讀、維護(hù)的高級(jí)計(jì)算機(jī)語(yǔ)言所寫(xiě)作的源代碼程序,翻譯為計(jì)算機(jī)能解讀、運(yùn)行的低階機(jī)器語(yǔ)言的程序,也就是可執(zhí)行文件。編譯器將原始程序(source program)作為輸入,翻譯產(chǎn)生使用目標(biāo)語(yǔ)言(target language)的等價(jià)程序。源代碼一般為高級(jí)語(yǔ)言(High-level language),如Pascal、C、C++、C# 、Java等,而目標(biāo)語(yǔ)言則是匯編語(yǔ)言或目標(biāo)機(jī)器的目標(biāo)代碼(Object code),有時(shí)也稱作機(jī)器代碼(Machine code)。
一個(gè)現(xiàn)代編譯器的主要工作流程要通常要經(jīng)過(guò)預(yù)處理、編譯、匯編、鏈接等步驟。關(guān)于編譯器的工作流程的介紹,可參考:從C源代碼到可執(zhí)行文件的四個(gè)過(guò)程:預(yù)處理、編譯、匯編、鏈接
中間語(yǔ)言
中間語(yǔ)言(Intermediate language),有時(shí)也稱為中間表示(Intermediate Rrepresentation,IR)在計(jì)算機(jī)科學(xué)中,是指一種應(yīng)用于抽象機(jī)器(abstract machine)的編程語(yǔ)言,它設(shè)計(jì)的目的,是用來(lái)幫助我們分析計(jì)算機(jī)程序。這個(gè)術(shù)語(yǔ)源自于編譯器,在編譯器將源代碼編譯為目的碼的過(guò)程中,會(huì)先將源代碼轉(zhuǎn)換為一個(gè)或多個(gè)的中間表述,以方便編譯器進(jìn)行最佳化,并最終產(chǎn)生出目的機(jī)器的機(jī)器語(yǔ)言。通常,中間語(yǔ)言的設(shè)計(jì)與一般的機(jī)器語(yǔ)言有三個(gè)不同之處:
- 每個(gè)指令代表僅有一個(gè)基本的操作。舉例來(lái)說(shuō),在微處理器中出現(xiàn)的 shift-add 定址模式在中間語(yǔ)言不會(huì)出現(xiàn)。
- 指令集內(nèi)可能不會(huì)包含控制流程的資訊。
- 暫存器可用的數(shù)量可能會(huì)很大,甚至沒(méi)有限制。
最常見(jiàn)的中間語(yǔ)言表述形式,是三位址碼(Three address code),常簡(jiǎn)稱為 TAC 或 3AC。
這個(gè)術(shù)語(yǔ)也同時(shí)用來(lái)代稱一些作為中間層的語(yǔ)言,有些高級(jí)語(yǔ)言不會(huì)輸出為機(jī)器語(yǔ)言,它們僅會(huì)輸出這種中間語(yǔ)言,而這些中間語(yǔ)言則會(huì)像一般語(yǔ)言一樣,提交給編譯器,編譯為機(jī)器語(yǔ)言。這通常被用于讓最佳化的過(guò)程更簡(jiǎn)單,也用于增進(jìn)可移植性的能力,改進(jìn)移植的方式則是利用中間語(yǔ)言的編譯器,可以編譯出許多中央處理器及操作系統(tǒng)可使用的機(jī)器碼,例如C語(yǔ)言。中間語(yǔ)言的復(fù)雜度,通常介于高階語(yǔ)言及低級(jí)語(yǔ)言之間,例如匯編語(yǔ)言。
四元式
四元式主要由四部分組成:OP,arg1,arg2,result,即(操作符,操作數(shù)1,操作數(shù)2,結(jié)果),
其中,OP是運(yùn)算符,arg1,arg2分別是第一和第二個(gè)運(yùn)算對(duì)象,result是編譯程序?yàn)榇娣胖虚g運(yùn)算結(jié)果而引進(jìn)的變量,常稱為臨時(shí)變量。當(dāng)OP是一目運(yùn)算時(shí),常常將運(yùn)算對(duì)象定義為arg1。
例如 X = a*b+c/d 的四元式序列:
- 四元式出現(xiàn)的順序和語(yǔ)法成份的計(jì)值順序相一致。
- 四元式之間的聯(lián)系是通過(guò)臨時(shí)變量實(shí)現(xiàn)的,這樣易于調(diào)整和變動(dòng)四元式。
- 便于優(yōu)化處理。
三地址碼
三地址代碼是四元式的另一種表示形式。每個(gè)三地址碼指令,都可以被分解為一個(gè)四元式(4-tuple)。因?yàn)槊總€(gè)陳述都包含了三個(gè)變量,所以它被稱為三地址碼。
上面例子 X = a*b+c/d 的三地址序列:
常用的三地址碼(三地址碼形式和四元組形式)
| 1 | 賦值指令 | x = y op zx = op y | op為運(yùn)算符 |
| 2 | 復(fù)制指令 | x = y | |
| 3 | 條件跳轉(zhuǎn) | if x relop y goto n | relop為關(guān)系運(yùn)算符 |
| 4 | 非條件跳轉(zhuǎn) | goto n | 跳轉(zhuǎn)到地址n的指令 |
| 5 | 參數(shù)傳遞 | param x | 將x設(shè)置為參數(shù) |
| 6 | 過(guò)程調(diào)用 | call p,n | p為過(guò)程的名字n為過(guò)程的參數(shù)的個(gè)數(shù) |
| 7 | 過(guò)程返回 | return x | |
| 8 | 數(shù)組引用 | x=y[i] | i為數(shù)組的偏移地址,而不是下標(biāo) |
| 9 | 數(shù)組賦值 | x[i]=y | |
| 10 | 地址及指針操作 | x=&yx=*y *x=y |
將上表的三地址指令用四元式表示
| x = op y | ( op , y , _ , x) |
| x = y | ( = , y , _ , x) |
| if x relop y goto n | ( relop , x , y , n) |
| goto n | ( goto , _ , _ , n) |
| param x | ( param , _ , _ , x) |
| call p,n | ( call , p , n , _) |
| return x | ( return , _ , _ , x) |
| x=y[i] | ( =[] , y , i , x) ps: y為基地址,i為偏移地址 |
| x[i]=y | ( []= , y , x , i) |
| x=&y | ( & , y , _ , x) |
| x=*y | ( =* , y , _ , x) |
| *x=y | ( *= , y , _ , x) |
每一個(gè)指令只有一個(gè)操作符,那么只完成一個(gè)動(dòng)作,這樣看來(lái),三地址指令序列唯一確定了運(yùn)算完成的順序。
中間代碼生成的例子
while a<b doif c<5 thenwhile x>y doz=x+1;else x=y;100到112為指令的編碼,從100到112順序執(zhí)行。
| 101: ( j , -, -, 112 ) | 該指令為無(wú)條件指令,跳轉(zhuǎn)到112 |
| 102: ( j<, c, 5, 104 ) | 如果c<5 ,那么跳轉(zhuǎn)到104指令,否則繼續(xù)執(zhí)行103指令 |
| 103: ( j , -, - , 110 ) | 該指令為無(wú)條件指令,跳轉(zhuǎn)到110 |
| 104: ( j>, x, y, 106 ) | 如果x>y ,那么跳轉(zhuǎn)到106指令,否則繼續(xù)執(zhí)行105指令 |
| 105: ( j , -, - , 100 ) | 該指令為無(wú)條件指令,跳轉(zhuǎn)到100 |
| 106: ( + , x, 1 , t1 ) | x+1的值賦值給t1 |
| 107: ( = , t1, - , z ) | t1的值賦值給z,106和107完成了一條語(yǔ)句 |
| 108: ( j , -, - , 104 ) | 該指令為無(wú)條件指令,跳轉(zhuǎn)到104 |
| 109: ( j , -, - , 100 ) | 該指令為無(wú)條件指令,跳轉(zhuǎn)到100 |
| 110: (= , y, - , x ) | 把y賦值給x,然后執(zhí)行111指令 |
| 111: ( j , -, - , 100 ) | 該指令為無(wú)條件指令,跳轉(zhuǎn)到100 |
| 112: | 結(jié)束 |
中間表示IR的演變歷史
計(jì)算機(jī)科學(xué)家提出三地址代碼的理由如下:三地址代碼是一種線性IR。由于輸入源程序及輸出目標(biāo)程序都是線性的,因此,線性IR有著其他形式無(wú)法比擬的優(yōu)勢(shì)。另外,相對(duì)于其他表示形式而言,程序員對(duì)于線性表示形式通常會(huì)有一種莫名的親切感,編譯器設(shè)計(jì)者當(dāng)然也不例外。早期編譯器設(shè)計(jì)者往往都是匯編語(yǔ)言程序設(shè)計(jì)的高手,可以非常自然、流暢地閱讀線性的三地址代碼形式。同時(shí),線性表示形式也會(huì)降低輸入輸出的實(shí)現(xiàn)難度。隨著編譯器"端"、"遍"等概念的出現(xiàn),IR已經(jīng)不僅僅是一種存儲(chǔ)在內(nèi)存中的數(shù)據(jù)結(jié)構(gòu)。有時(shí)它也需要以文件形式轉(zhuǎn)存輸出,作為接口供其他系統(tǒng)讀取使用。
那么,一定有讀者會(huì)心存疑問(wèn):為什么將其設(shè)計(jì)為"三地址"的形式呢?實(shí)際上,這是計(jì)算機(jī)科學(xué)家經(jīng)過(guò)多年實(shí)踐探索后才得到共識(shí)的。三地址代碼并不是唯一的線性IR,只能說(shuō)是最為常見(jiàn)的而已。在編譯技術(shù)領(lǐng)域,二地址代碼、單地址代碼(即棧式機(jī)代碼)都曾出現(xiàn)過(guò),也曾在某些應(yīng)用領(lǐng)域盛行一時(shí),尤其是單地址代碼。
二地址代碼比較簡(jiǎn)單,就是選擇其中一個(gè)對(duì)象同時(shí)充當(dāng)運(yùn)算分量與目標(biāo)操作數(shù)。在早期,二地址代碼主要就是著眼于x86機(jī)器而提出的。不過(guò),實(shí)踐證明,這只是人們的一廂情愿而已,即使是針對(duì)x86機(jī)器,二地址的優(yōu)勢(shì)也并不明顯,它反而可能會(huì)給編譯器帶來(lái)一定的麻煩,所以這種表示形式已經(jīng)逐步被淘汰了。
然而,單地址代碼的情況則截然不同了,在現(xiàn)代編譯器設(shè)計(jì)中,單地址代碼也是應(yīng)用比較廣泛的一種IR。尤其是近年隨著混合語(yǔ)言的日漸壯大,單地址代碼也重新進(jìn)入了人們的視野。由于執(zhí)行單地址代碼程序的棧式機(jī)架構(gòu)相對(duì)比較簡(jiǎn)單,可以非常方便地構(gòu)造相關(guān)的解釋器或虛擬機(jī),所以單地址代碼深受混合語(yǔ)言設(shè)計(jì)者的歡迎。讀者熟悉的Java字節(jié)碼、.NET的IL都是單地址代碼。棧式機(jī)或者單地址代碼與常見(jiàn)的x86體系結(jié)構(gòu)相差甚遠(yuǎn),可能讀者所知不多。不過(guò),單地址代碼還是一種比較有意思的表示形式,因此,筆者想通過(guò)一個(gè)簡(jiǎn)單的實(shí)例讓讀者對(duì)單地址代碼有所了解。
三地址代碼是在二地址代碼的基礎(chǔ)上發(fā)展而來(lái)的。二地址代碼的不足之處在于它通常會(huì)給其中一個(gè)源操作分量帶來(lái)一定副作用。當(dāng)然,這種設(shè)計(jì)的靈感最初是來(lái)源于x86指令系統(tǒng)的,但是卻忘了一個(gè)重要的區(qū)別:x86指令中往往都是以寄存器作為暫存空間的。而暫存空間對(duì)于二地址代碼卻是一個(gè)棘手的問(wèn)題。為了解決二地址代碼的不足,人們提出了一個(gè)對(duì)源操作分量不產(chǎn)生任何副作用的形式,那就是三地址代碼。也就是說(shuō),在一行三地址代碼中,任何運(yùn)算都不會(huì)改變兩個(gè)源操作分量。這是三地址代碼與二地址代碼的主要區(qū)別。這個(gè)特性是非常重要的,它將使得編譯器更自由地復(fù)用名字與值,不必考慮代碼帶來(lái)的副作用。
一般來(lái)說(shuō),三地址代碼的大多數(shù)操作都是由四項(xiàng)組成,即一個(gè)操作碼和三個(gè)地址。不過(guò),三地址代碼同樣存在級(jí)別差異。隨著語(yǔ)言復(fù)雜性的提高,在現(xiàn)代編譯器設(shè)計(jì)中,三地址代碼的級(jí)別概念顯得尤其重要。根據(jù)編譯器設(shè)計(jì)的需要,有些三地址代碼可能近似于源語(yǔ)言,而有些三地址代碼則更接近于目標(biāo)語(yǔ)言。當(dāng)然,級(jí)別主要就是取決于三地址代碼的操作符及操作分量的復(fù)雜性。下面,筆者就操作符及操作分量這兩個(gè)話題來(lái)討論三地址代碼。
操作符是用于標(biāo)識(shí)三地址代碼操作含義的元素。根據(jù)源語(yǔ)言、目標(biāo)語(yǔ)言的特點(diǎn),三地址代碼操作符的集合以及抽象程度是各不相同的。其中,抽象程度是三地址代碼設(shè)計(jì)中的重要因素之一。一般而言,三地址代碼將包含大部分低級(jí)操作,即目標(biāo)機(jī)所支持的指令。不過(guò),這并不意味著三地址代碼就是機(jī)器指令系統(tǒng)的映射。設(shè)計(jì)者應(yīng)該從便于后端處理的角度考慮,盡可能地發(fā)揮三地址代碼作為中間語(yǔ)言的作用。
Ref:
https://zh.m.wikipedia.org/wiki/%E4%B8%89%E4%BD%8D%E5%9D%80%E7%A2%BC
https://baike.baidu.com/item/%E4%B8%89%E5%9C%B0%E5%9D%80%E7%A0%81/23121007
https://blog.csdn.net/starter_____/article/details/90146048
https://jishuin.proginn.com/p/763bfbd55cbd
https://book.51cto.com/art/201206/340208.htm
總結(jié)
- 上一篇: 沂源醉美双泉酒庄生产什么酒?
- 下一篇: Yapi Mock 远程代码执行漏洞