本發(fā)明涉及計(jì)算機(jī)領(lǐng)域,特別是涉及一種程序編譯方法和編譯器。
背景技術(shù):
目前,按照統(tǒng)計(jì)顯示,程序中使用最多的一類指令就是內(nèi)存訪問(wèn)的指令。在待編譯的程序,比如C源代碼中,對(duì)于一個(gè)結(jié)構(gòu)體的訪問(wèn)通常會(huì)被編譯器翻譯為對(duì)應(yīng)的內(nèi)存訪問(wèn)指令。不同的架構(gòu)指令集的內(nèi)存訪問(wèn)指令一般都屬于以下兩種的類型:第一種是通過(guò)一個(gè)寄存器(即,基地址寄存器)加上一個(gè)常量偏移地址訪問(wèn)一塊內(nèi)存地址的內(nèi)容,第二種是通過(guò)一個(gè)寄存器(即,基地址寄存器)加上另外一個(gè)寄存器(即,偏移寄存器)的地址訪問(wèn)一塊內(nèi)存地址的內(nèi)容。例如,在AArch64架構(gòu)指令集中,ldr w8,[x1,#4],屬于第一類的內(nèi)存訪問(wèn)指令,表示把寄存器x1的內(nèi)容加上常數(shù)4,把得到的結(jié)果作為內(nèi)存地址,并將該地址的內(nèi)容讀入到寄存器w8中;而ldr w8,[x1,x2],則屬于第二類的內(nèi)存訪問(wèn)指令,表示將寄存器x1的內(nèi)容加上寄存器x2的內(nèi)容,得到的結(jié)果作為內(nèi)存地址,并將該地址的內(nèi)容讀入到寄存器w8中。在PI32-V2架構(gòu)指令集中,也有類似的第一類指令,如r8=[r1+4],表示r8的寄存器是基地址r1加上4的內(nèi)容;第二類指令,如r8=[r1+r2],表示r8的寄存器是基地址r1加上偏移寄存器r2。
由于每條指令需要一定長(zhǎng)度的編碼,而這個(gè)編碼是有限的,所以通常而言,第一類內(nèi)存訪問(wèn)指令的常數(shù)偏移范圍是有限制的,例如AArch64中,第一類指令的偏移范圍是-128×4到128×4。如果我們需要訪問(wèn)的偏移地址比限定的范圍要大,我們只能首先利用一條移動(dòng)常數(shù)的指令,把偏移移動(dòng)到一個(gè)寄存器中,然后再通過(guò)第二條指令來(lái)訪問(wèn)。這一多余的步驟會(huì)使得代碼尺寸變大。另外一方面,一些指令集中,如PI32-V2中,第一類指令會(huì)有兩種不同的編碼形式,一種是短編碼形式,只能編碼更短的偏移范圍,另外一種是長(zhǎng)編碼形式,可以編碼更長(zhǎng)一些的偏移范圍。如果一個(gè)程序中生成了更多的長(zhǎng)編碼形式的第一類指令,也會(huì)使得最終的代碼尺寸變大。
傳統(tǒng)的編譯技術(shù)中,對(duì)于結(jié)構(gòu)體訪問(wèn),通常會(huì)生成上述的內(nèi)存訪問(wèn)指令,例如,對(duì)于C源代碼中的f.a=10;f.b=10;假設(shè)f的地址儲(chǔ)存在r0,域a的偏移是80,域b的偏移是84,則對(duì)于PI32-V2指令集,這條賦值會(huì)翻譯三條匯編指令為:r1=10;[r0+80]=r1;[r0+84]=r1;這里生成的指令是第一類中的長(zhǎng)編碼形式的指令。在實(shí)際程序中,這樣的賦值語(yǔ)句往往比較多,且會(huì)比較集中,這樣傳統(tǒng)的方法會(huì)生成比較多的長(zhǎng)編碼形式的指令,使得代碼尺寸較大。
技術(shù)實(shí)現(xiàn)要素:
基于此,有必要提供一種程序編譯方法和編譯器,可以減少內(nèi)存訪問(wèn)指令的編碼尺寸。
一種程序編譯方法,所述方法包括:
讀取待編譯的程序,并記錄每條內(nèi)存訪問(wèn)指令的基地址以及偏移地址,所述內(nèi)存訪問(wèn)指令的內(nèi)存地址等于該指令的基地址加上該指令的偏移地址;
將基地址相同的所述內(nèi)存訪問(wèn)指令分為一個(gè)大類;
將各大類中偏移地址最小的指令的內(nèi)存地址作為該大類中所有所述指令的新的基地址;
根據(jù)各大類的新的基地址計(jì)算各大類使用新的基地址是否能使該大類的內(nèi)存訪問(wèn)指令的總代碼長(zhǎng)度變??;若存在總代碼長(zhǎng)度變小的大類,則修改所述待編譯的程序的代碼,將這些大類中的每條所述指令的基地址修改成新的基地址、偏移地址修改為新的偏移地址,以生成目標(biāo)程序。
在其中一個(gè)實(shí)施例中,所述將基地址相同的所述內(nèi)存訪問(wèn)指令分為一個(gè)大類的步驟,還包括將各大類中的所述內(nèi)存訪問(wèn)指令按偏移地址從小到大進(jìn)行排序。
在其中一個(gè)實(shí)施例中,所述將各大類中偏移地址最小的指令的內(nèi)存地址作為該大類中所有所述指令的新的基地址的步驟之后,還包括將各大類中的所述指令進(jìn)一步分組的步驟;
將一個(gè)大類中的所述指令進(jìn)一步分組的步驟包括:
步驟A,按照偏移地址從小到大查看該大類中的所述指令,并將以所述新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度是最短編碼長(zhǎng)度的所述指令加入到以所述新的基地址為基地址的組中;
步驟B,當(dāng)查看到以所述新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度不是最短編碼長(zhǎng)度的內(nèi)存訪問(wèn)指令,則以該指令的內(nèi)存地址作為新的基地址創(chuàng)建一個(gè)新的組;
重復(fù)步驟A、B直到將該大類中的所有指令分組完畢。
在其中一個(gè)實(shí)施例中,所述步驟B是:
當(dāng)查看到以所述新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度不是最短編碼長(zhǎng)度的內(nèi)存訪問(wèn)指令,則
嘗試在保證當(dāng)前組的指令的編碼長(zhǎng)度不變的前提下、將當(dāng)前組的基地址增大以使當(dāng)前查看到的內(nèi)存訪問(wèn)指令的偏移地址達(dá)到最短編碼長(zhǎng)度,并將增大后的基地址作為當(dāng)前組的新的基地址;
若嘗試不成功,則以該指令的內(nèi)存地址作為新的基地址創(chuàng)建一個(gè)新的組。
在其中一個(gè)實(shí)施例中,所述修改所述待編譯的程序的代碼,將這些大類中的每條所述指令的基地址修改成新的基地址、偏移地址修改為新的偏移地址,以生成目標(biāo)程序的步驟,具體為:
將每一大類的修改基地址的指令插入至所述目標(biāo)程序的目標(biāo)位置處;其中,所述目標(biāo)位置為使得每一大類的修改基地址的指令在該大類中所有所述指令之前被執(zhí)行的位置;
將這些大類中的每條所述指令的基地址修改成新的基地址、偏移地址修改為新的偏移地址。
在其中一個(gè)實(shí)施例中,所述讀取待編譯的程序,并記錄每條內(nèi)存訪問(wèn)指令的基地址以及偏移地址的步驟,具體為:
讀取待編譯的程序,并將所述待編譯的程序劃分成若干函數(shù);
記錄每一函數(shù)中每條內(nèi)存訪問(wèn)指令的基地址以及偏移地址。
一種程序編譯器,所述編譯器包括:
程序加載模塊,該程序加載模塊用于讀取待編譯的程序,并記錄每條內(nèi)存訪問(wèn)指令的基地址以及偏移地址;所述內(nèi)存訪問(wèn)指令的內(nèi)存地址等于該指令的基地址加上該指令的偏移地址;
基地址變換模塊,該基地址變換模塊的輸入端與所述程序加載模塊的輸出端相連接,該基地址變換模塊用于將基地址相同的所述內(nèi)存訪問(wèn)指令分為一個(gè)大類,且將各大類中偏移地址最小的指令的內(nèi)存地址作為該大類中所有所述指令的新的基地址;
編碼長(zhǎng)度計(jì)算模塊,該編碼長(zhǎng)度計(jì)算模塊的輸入端與基地址變換模塊的輸出端相連接,該編碼長(zhǎng)度計(jì)算模塊用于根據(jù)各大類的新的基地址計(jì)算各大類使用新的基地址后,該大類的內(nèi)存訪問(wèn)指令的總代碼長(zhǎng)度;
代價(jià)判斷模塊,該代價(jià)判斷模塊的輸入端與所述編碼長(zhǎng)度計(jì)算模塊的輸出端相連接,該代價(jià)判斷模塊用于根據(jù)各大類的新的基地址計(jì)算各大類使用新的基地址是否能使該大類的內(nèi)存訪問(wèn)指令的總代碼長(zhǎng)度變??;
目標(biāo)程序生成模塊,該目標(biāo)程序生成模塊的輸入端與所述代價(jià)判斷模塊的輸出端相連接,該目標(biāo)程序生成模塊用于在存在總代碼長(zhǎng)度變小的大類,則修改所述待編譯的程序的代碼,將這些大類中的每條所述指令的基地址修改成新的基地址、偏移地址修改為新的偏移地址,以生成目標(biāo)程序。
在其中一個(gè)實(shí)施例中,還包括:
排序模塊,該排序模塊的輸入端與所述基地址變換模塊的輸出端相連接,該排序模塊的輸出端與所述編碼長(zhǎng)度計(jì)算模塊的輸入端相連接,該排序模塊用于將各大類中的所述內(nèi)存訪問(wèn)指令按偏移地址從小到大進(jìn)行排序。
在其中一個(gè)實(shí)施例中,所述基地址變換模塊包括:
分組控制單元,該分組控制單元的輸入端與所述排序模塊的輸出端相連接,該分組控制單元用于判斷該大類中的所有指令是否分組完畢;
第一分組單元,該第一分組單元的輸入端與分組控制單元的輸出端相連接,該第一分組單元用于在該大類中的所有指令未分組完畢時(shí),按照偏移地址從小到大查看該大類中的所述指令,并將以所述新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度是最短編碼長(zhǎng)度的所述指令加入到以所述新的基地址為基地址的組中;
第二分組單元,該第二分組單元的輸入端與所述第一分組單元的輸出端相連接,該第二分組單元用于在查看到以所述新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度不是最短編碼長(zhǎng)度的內(nèi)存訪問(wèn)指令,則以該指令的內(nèi)存地址作為新的基地址創(chuàng)建一個(gè)新的組。
在其中一個(gè)實(shí)施例中,所述第二分組單元包括:
編碼長(zhǎng)度判斷子單元,該編碼長(zhǎng)度判斷子單元的輸入端與所述第一分組單元的輸出端相連接,該編碼長(zhǎng)度判斷單元用于查看是否存在以所述新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度不是最短編碼長(zhǎng)度的內(nèi)存訪問(wèn)指令;
分組子單元,該分組子單元的輸入端與該編碼長(zhǎng)度判斷子單元的輸出端相連接,該分組子單元用于當(dāng)查看到以所述新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度不是最短編碼長(zhǎng)度的內(nèi)存訪問(wèn)指令,則嘗試在保證當(dāng)前組的偏移地址的編碼長(zhǎng)度不變的前提下、將當(dāng)前組的基地址增大以使當(dāng)前查看到的內(nèi)存訪問(wèn)指令的偏移地址達(dá)到最短編碼長(zhǎng)度,并將增大后的基地址作為當(dāng)前組的新的基地址,若嘗試不成功,則以該指令的內(nèi)存地址作為新的基地址創(chuàng)建一個(gè)新的組。
在其中一個(gè)實(shí)施例中,所述目標(biāo)程序生成模塊還用于將每一大類的修改基地址的指令插入至所述目標(biāo)程序的目標(biāo)位置處;其中,所述目標(biāo)位置為使得每一大類的修改基地址的指令在該大類中所有所述指令之前被執(zhí)行的位置。
在其中一個(gè)實(shí)施例中,所述程序加載模塊包括:
程序讀取單元,用于讀取待編譯的程序;
程序劃分單元,該程序劃分單元的輸入端與該程序讀取單元的輸出端相連接,該程序劃分單元用于將所述待編譯的程序劃分成若干函數(shù);
記錄單元,該記錄單元的輸入端與所述程序劃分單元的輸出端相連接,該記錄單元用于記錄每一函數(shù)中每條內(nèi)存訪問(wèn)指令的基地址以及偏移地址。
上述的程序編譯方法和編譯器,通過(guò)將基地址相同的指令歸為一個(gè)集合后,重新選擇該集合中的指令的基地址,使得內(nèi)存訪問(wèn)指令的偏移減少,從而縮減內(nèi)存訪問(wèn)指令的尺寸,進(jìn)而減少了總的程序的尺寸,應(yīng)用范圍較為廣泛。
附圖說(shuō)明
圖1為一實(shí)施例中計(jì)算機(jī)程序的編譯流程;
圖2為一實(shí)施例中程序編譯方法的流程圖;
圖3為一實(shí)施例中程序編譯器的結(jié)構(gòu)示意圖。
其中,
100 程序加載模塊
110 程序讀取單元
120 程序劃分單元
130 記錄單元
200 基地址變換模塊
210 第一分組單元
220 第二分組單元
230 分組控制單元
221 編碼長(zhǎng)度判斷子單元
222 分組子單元
300 編碼長(zhǎng)度計(jì)算模塊
400 代價(jià)判斷模塊
500 目標(biāo)程序生成模塊
600 排序模塊
具體實(shí)施方式
為了使本發(fā)明的目的、技術(shù)方案及優(yōu)點(diǎn)更加清楚明白,以下結(jié)合附圖及實(shí)施例,對(duì)本發(fā)明進(jìn)行進(jìn)一步詳細(xì)說(shuō)明。應(yīng)當(dāng)理解,此處所描述的具體實(shí)施例僅僅用于解釋本發(fā)明,并不用于限定本發(fā)明。
在詳細(xì)說(shuō)明根據(jù)本發(fā)明的實(shí)施例前,應(yīng)該注意到的是,所述的實(shí)施例主要在于與程序編譯方法和編譯器相關(guān)的步驟和系統(tǒng)組件的組合。因此,所屬系統(tǒng)組件和方法步驟已經(jīng)在附圖中通過(guò)常規(guī)符號(hào)在適當(dāng)?shù)奈恢帽硎境鰜?lái)了,并且只示出了與理解本發(fā)明的實(shí)施例有關(guān)的細(xì)節(jié),以免因?qū)τ诘靡嬗诒景l(fā)明的本領(lǐng)域普通技術(shù)人員而言顯而易見的那些細(xì)節(jié)模糊了本發(fā)明的公開內(nèi)容。
在本文中,諸如左和右,上和下,前和后,第一和第二之類的關(guān)系術(shù)語(yǔ)僅僅用來(lái)區(qū)分一個(gè)實(shí)體或動(dòng)作與另一個(gè)實(shí)體或動(dòng)作,而不一定要求或暗示這種實(shí)體或動(dòng)作之間的任何實(shí)際的這種關(guān)系或順序。術(shù)語(yǔ)“包括”、“包含”或任何其他變體旨在涵蓋非排他性的包含,由此使得包括一系列要素的過(guò)程、方法、物品或者設(shè)備不僅包含這些要素,而且還包含沒(méi)有明確列出的其他要素,或者為這種過(guò)程、方法、物品或者設(shè)備所固有的要素。
請(qǐng)參閱圖1所示,圖1為一實(shí)施例中計(jì)算機(jī)程序的編譯流程,在該實(shí)施例中,待編譯的程序經(jīng)過(guò)前端分析得到中間表示,中間表示經(jīng)過(guò)若干次的中端優(yōu)化后,得到最終的中間表示,最后對(duì)該中間表示進(jìn)行代碼生成,得到最后的目標(biāo)程序。本發(fā)明中的編譯方法可以是整個(gè)圖1中所示的編譯流程,也可以僅僅是其中將一個(gè)中間表示轉(zhuǎn)換成另一個(gè)中間表示的過(guò)程。在本實(shí)施例以及后文中以一段C源代碼作為例子以說(shuō)明該實(shí)施例以及該發(fā)明。假設(shè)源程序?yàn)椋?/p>
在經(jīng)過(guò)上述的前端分析和若干次中端優(yōu)化后,假設(shè)在進(jìn)行本發(fā)明的方法前,上述源程序被翻譯為PI32-V2指令后的中間表示為:
foobar:
v1=r1
v0=r0
v2=[v0+256]
v3=[v1+256]
v4=v3+v2
v5=[v0+260]
v6=v4+v5
v7=[v1+260]
v8=v6+v7
v9=[v0+264]
v10=v8+v9
v11=[v1+264]
v12=v10+v11
v13=[v1+268]
v14=v13<<1
v15=v12+v14
r0=v15
Rts
此時(shí)還未進(jìn)行寄存器的分配,其中r0、r1表示物理寄存器,r0是第一個(gè)參數(shù),r1是第二個(gè)參數(shù),v0,v1,……,v15是虛擬寄存器,此時(shí)只有一個(gè)foobar函數(shù),因此待編譯的程序只有該foobar函數(shù)。本發(fā)明中的編譯方法主要是通過(guò)收集一段程序(例如,一個(gè)函數(shù)體內(nèi))的內(nèi)存訪問(wèn)指令,然后選擇恰當(dāng)?shù)幕刂?,并在恰?dāng)?shù)奈恢貌迦胄碌幕刂酚?jì)算指令,使得內(nèi)存訪問(wèn)指令的偏移可以減少,最終達(dá)到縮減總的代碼尺寸的目的。
請(qǐng)參閱圖2所示,圖2為一實(shí)施例中程序編譯方法的流程圖,在該實(shí)施例中,該方法可以包括:
S202:讀取待編譯的程序,并記錄每條內(nèi)存訪問(wèn)指令的基地址以及偏移地址??梢钥闯鰞?nèi)存訪問(wèn)指令的內(nèi)存地址等于該指令的基地址加上該指令的偏移地址,在該實(shí)施例中,首先要逐條檢查函數(shù)體的指令,而后可以得到內(nèi)存訪問(wèn)指令及其大致情況。例如在上述的實(shí)施例中,內(nèi)存訪問(wèn)指令有:
v2=[v0+256]
v3=[v1+256]
v5=[v0+260]
v7=[v1+260]
v9=[v0+264]
v11=[v1+264]
v13=[v1+268]
在其中一個(gè)實(shí)施例中,讀取待編譯的程序,并記錄每條內(nèi)存訪問(wèn)指令的基地址以及偏移地址的步驟,可以包括讀取待編譯的程序,并將待編譯的程序劃分成若干函數(shù)。記錄每一函數(shù)中每條內(nèi)存訪問(wèn)指令的基地址以及偏移地址。在本實(shí)施例中,可以以函數(shù)為基本單位,即該方法中每次只考慮一個(gè)函數(shù)范圍內(nèi)的內(nèi)存訪問(wèn)指令,且該方法在執(zhí)行寄存器分配前進(jìn)行。一般在編譯器內(nèi)部,一個(gè)函數(shù)被標(biāo)識(shí)為一些基本塊的集合,一個(gè)基本塊被表示為一系列的指令。
S204:將基地址相同的內(nèi)存訪問(wèn)指令分為一個(gè)大類。在該實(shí)施例中,以基地址寄存器作為大類的劃分依據(jù),因此將基地址相同的內(nèi)存訪問(wèn)指令分為一大類。例如在上述的實(shí)施例中包括兩個(gè)大類:
第一大類,以v0為原始的基地址:
v2=[v0+256]
v5=[v0+260]
v9=[v0+264]
第二大類,以v1為原始的基地址:
v3=[v1+256]
v7=[v1+260]
v11=[v1+264]
v13=[v1+268]
S206:將各大類中偏移地址最小的指令的內(nèi)存地址作為該大類中所有指令的新的基地址。例如上述實(shí)施例中,偏移地址最小為256,因此以該偏移地址最小的指令的內(nèi)存地址為該組中所有指令的新的基地址,即第一大類中,以v2=[v0+256]為新的基地址的指令包含:
v2=[v0+256]該指令的新的偏移地址為+0。
v5=[v0+260]該指令的新的偏移地址為+4。
v9=[v0+264]該指令的新的偏移地址為+8。
第二大類中,以v3=[v1+256]為新的基地址的指令包含:
v3=[v1+256]該指令的新的偏移地址為0。
v7=[v1+260]該指令的新的偏移地址為+4。
v11=[v1+264]該指令的新的偏移地址為+8。
v13=[v1+268]該指令的新的偏移地址為+12。
S208:根據(jù)各大類的新的基地址計(jì)算各大類使用新的基地址是否能使該大類的內(nèi)存訪問(wèn)指令的總代碼長(zhǎng)度變小。
S210:若存在總代碼長(zhǎng)度變小的大類,則修改所述待編譯的程序的代碼,將這些大類中的每條所述指令的基地址修改成新的基地址、偏移地址修改為新的偏移地址,以生成目標(biāo)程序。
具體地,在上述的例子中,第一大類中的指令,需要增加一條修改基地址的指令:
v16=v0+256增加了該條加法指令,以修改該第一大類中的所有指令的基地址,該指令為一條長(zhǎng)編碼指令,代價(jià)是4字節(jié)。
且在該例子中,第一大類中的內(nèi)存訪問(wèn)指令相應(yīng)地修改為:
v2=[v16+0]該條指令相比于原來(lái)的指令v2=[v0+256]減少2字節(jié)。
v5=[v16+4]該條指令相比于原來(lái)的指令v5=[v0+260]減少2字節(jié)。
v9=[v16+8]該條指令相比于原來(lái)的指令v9=[v0+264]減少2字節(jié)。
因此綜上分析,在第一大類中的指令經(jīng)過(guò)編譯后可以減少2字節(jié)的代碼空間。
即將上述實(shí)施例中的基地址從v0修改為v0+256處需要一條加法指令,或者需要額外的4字節(jié)的代價(jià),但同時(shí)能夠把該第一大組中的其他三條指令的代價(jià)每條減少2個(gè)字節(jié),所以這是有利可圖的。
同理可以得出第二大類中的指令可以減少4字節(jié)的代碼空間:
v17=v1+256增加了該條加法指令,以修改該第一大類中的所有指令的基地址,該指令為一條長(zhǎng)編碼指令,代價(jià)是4字節(jié)。
且在該例子中,第二大類中的指令:
v3=[v17+0]該條指令相比于原來(lái)的指令v3=[v1+256]減少2字節(jié)。
v7=[v17+4]該條指令相比于原來(lái)的指令v7=[v1+260]減少2字節(jié)。
v11=[v17+8]該條指令相比于原來(lái)的指令v11=[v1+264]減少2字節(jié)。
v13=[v17+12]該條指令相比于原來(lái)的指令v13=[v1+268]減少2字節(jié)。
上述的程序編譯方法,通過(guò)將基地址相同的指令歸為一個(gè)集合后,重新選擇該集合中的指令的基地址,使得內(nèi)存訪問(wèn)指令的偏移減少,從而縮減內(nèi)存訪問(wèn)指令的尺寸,進(jìn)而減少了總的程序的尺寸,應(yīng)用范圍較為廣泛。
在其中一個(gè)實(shí)施例中,將基地址相同的內(nèi)存訪問(wèn)指令分為一個(gè)大類的步驟,即上述步驟S204,還可以包括將各大類中的內(nèi)存訪問(wèn)指令按照偏移地址從小到大進(jìn)行排序。例如上述第一大類中的三個(gè)指令可以按照其偏移地址256、260、264的順序進(jìn)行排序,即v2=[v0+256]、v5=[v0+260]、v9=[v0+264]。同理也可以對(duì)第二大類中的指令進(jìn)行排序,且排序的方式可以是按照偏移地址從小到大排列,也可以是按照偏移地址從大到小進(jìn)行排列,在此不再贅述。
在其中一個(gè)實(shí)施例中將各大類中偏移地址最小的指令的內(nèi)存地址作為該大類中所有所述指令的新的基地址的步驟之后,即步驟S206之后還可以包括將各大類中的指令進(jìn)一步分組的步驟。
在其中一個(gè)實(shí)施例中,根據(jù)將各大類中的指令進(jìn)一步分組的步驟,具體為:
步驟A,按照偏移地址從小到大查看該大類中的指令,并將以新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度是最短編碼長(zhǎng)度的指令加入到以新的基地址為基地址的組中。
例如,可以參閱上述實(shí)施例,對(duì)于第一大類中,首先按照偏移地址進(jìn)行排序,選取最小偏移地址所對(duì)應(yīng)的指令的地址為新的基地址,即偏移為256處,對(duì)于該第一大類中,包含的指令有:
v2=[v0+256]該指令的新的偏移地址為+0。
v5=[v0+260]該指令的新的偏移地址為+4。
v9=[v0+264]該指令的新的偏移地址為+8。
可以看出上述指令只需要短編碼形式的指令即可實(shí)現(xiàn),因此不需要再繼續(xù)分組。
步驟B,當(dāng)查看到以新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度不是最短編碼長(zhǎng)度的內(nèi)存訪問(wèn)指令,則以該指令的內(nèi)存地址作為新的基地址創(chuàng)建一個(gè)新的組。
例如,假設(shè)在第一大類中存在一個(gè)在采用新的基地址時(shí)、需要以長(zhǎng)編碼形式進(jìn)行編碼的內(nèi)存訪問(wèn)指令,則以該指令的內(nèi)存地址作為新的基地址創(chuàng)建一新的組。
重復(fù)步驟A、B直到將該大類中的所有指令分組完畢。
在其中一個(gè)實(shí)施例中,上述的步驟B可以包括:
當(dāng)查看到以新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度不是最短編碼長(zhǎng)度的內(nèi)存訪問(wèn)指令,則嘗試在保證當(dāng)前組的指令的編碼長(zhǎng)度不變的前提下、將當(dāng)前組的基地址增大以使當(dāng)前查看到的內(nèi)存訪問(wèn)指令達(dá)到最短編碼長(zhǎng)度,并將增大后的基地址作為當(dāng)前組的新的基地址。若嘗試不成功,則以該指令的內(nèi)存地址作為新的基地址創(chuàng)建一個(gè)新的組。例如,假設(shè)在第一大類中存在一個(gè)在采用新的基地址時(shí)、需要以長(zhǎng)編碼形式進(jìn)行編碼的內(nèi)存訪問(wèn)指令A(yù),則首先嘗試修改該新的基地址,在第一大類中,新的基地址為v2=[v0+256],可以嘗試修改該新的基地址為v5=[v0+260],則指令v2=[v0+256]在基地址為v5=[v0+260]的情況下的新的偏移地址為-4,指令v9=[v0+264]在基地址為v5=[v0+260]的情況下的新的偏移地址為+4,同理計(jì)算指令A(yù)的新的偏移地址,如果該指令A(yù)在基地址為v5=[v0+260]的情況下的編碼長(zhǎng)度為最短編碼長(zhǎng)度,則修改基地址v2=[v0+256]為v5=[v0+260],如果不存在這樣一個(gè)基地址,則可以以該指令的內(nèi)存地址作為新的基地址創(chuàng)建一個(gè)新的組,例如以指令A(yù)的內(nèi)存地址為新的基地址,或者繼續(xù)嘗試其他的基地址,例如以v9=[v0+264]為基地址等。
在其中一個(gè)實(shí)施例中,修改待編譯的程序的代碼,將這些大類中的每條指令的基地址修改成新的基地址、偏移地址修改為新的偏移地址,以生成目標(biāo)程序的步驟,具體可以為:將每一大類的修改基地址的指令插入至目標(biāo)程序的目標(biāo)位置處;其中,目標(biāo)位置為使得每一大類的修改基地址的指令在該大類中所有指令之前被執(zhí)行的位置。將這些大類中的每條所述指令的基地址修改成新的基地址、偏移地址修改為新的偏移地址。例如,從上述實(shí)施例中,函數(shù)foobar的執(zhí)行可以看出,插入新的基地址的指令的目標(biāo)位置處時(shí)在v0=r0之后,該位置能夠保證變換基地址的指令在其他的內(nèi)存訪問(wèn)指令被執(zhí)行之前而被執(zhí)行,因此經(jīng)過(guò)上述的編譯后,待編譯的程序最終轉(zhuǎn)換為:
foobar:
v1=r1
v0=r0
v16=v0+256
v17=v1+256
v2=[v16+0]
v3=[v17+0]
v4=v3+v2
v5=[v16+4]
v6=v4+v5
v7=[v17+4]
v8=v6+v7
v9=[v16+8]
v10=v8+v9
v11=[v17+8]
v12=v10+v11
v13=[v17+12]
v14=v13<<1
v15=v12+v14
r0=v15
rts
經(jīng)過(guò)上述分析可以看出,該目標(biāo)程序相較于原待編譯的程序減少了6個(gè)字節(jié)的代碼尺寸空間,且經(jīng)過(guò)試驗(yàn),以編譯Linux 3.10內(nèi)核到PI32-V2指令集為例,使用本發(fā)明中的方法比起傳統(tǒng)的編譯方法能夠減少大概2%的代碼尺寸。
請(qǐng)參閱圖3所示,圖3為一實(shí)施例中程序編譯器的結(jié)構(gòu)示意圖,在該實(shí)施例中,編譯器可以包括程序加載模塊100、基地址變換模塊200、編碼長(zhǎng)度計(jì)算模塊300、代價(jià)判斷模塊400以及目標(biāo)程序生成模塊500,該基地址變換模塊200的輸入端與程序加載模塊100的輸出端相連接,該編碼長(zhǎng)度計(jì)算模塊300的輸入端與基地址變換模塊200的輸出端相連接,該代價(jià)判斷模塊400的輸入端與編碼長(zhǎng)度計(jì)算模塊300的輸出端相連接,該目標(biāo)程序生成模塊500的輸入端與代價(jià)判斷模塊400的輸出端相連接。
其中,該程序加載模塊100用于讀取待編譯的程序,并記錄每條內(nèi)存訪問(wèn)指令的基地址以及偏移地址;該內(nèi)存訪問(wèn)指令的內(nèi)存地址等于該指令的基地址加上該指令的偏移地址。
該基地址變換模塊200用于將基地址相同的內(nèi)存訪問(wèn)指令分為一個(gè)大類,且將各大類中偏移地址最小的指令的內(nèi)存地址作為該大類中所有所述指令的新的基地址。
該編碼長(zhǎng)度計(jì)算模塊300用于根據(jù)各大類的新的基地址計(jì)算各大類使用新的基地址后,該大類的內(nèi)存訪問(wèn)指令的總代碼長(zhǎng)度。
該代價(jià)判斷模塊400用于根據(jù)各大類的新的基地址計(jì)算各大類使用新的基地址是否能使該大類的內(nèi)存訪問(wèn)指令的總代碼長(zhǎng)度變小。
該目標(biāo)程序生成模塊500用于在存在總代碼長(zhǎng)度變小的大類,則修改待編譯的程序的代碼,將這些大類中的每條所述指令的基地址修改成新的基地址、偏移地址修改為新的偏移地址,以生成目標(biāo)程序。
在其中一個(gè)實(shí)施例中,該編譯器還可以包括排序模塊600,該排序模塊600的輸入端與基地址變換模塊200的輸出端相連接,該排序模塊600的輸出端與編碼長(zhǎng)度計(jì)算模塊300的輸入端相連接,該排序模塊600用于將各大類中的內(nèi)存訪問(wèn)指令按照偏移地址從小到大進(jìn)行排序。
在其中一個(gè)實(shí)施例中,基地址變換模塊200可以包括第一分組單元210、第二分組單元220和分組控制單元230,該分組控制單元230的輸入端與排序模塊600的輸出端相連接,該第一分組單元210的輸入端與分組控制單元230的輸出端相連接,該第二分組單元220的輸入端與第一分組單元210的輸出端相連接。
其中,該分組控制單元230用于判斷該大類中的所有指令是否分組完畢。
該第一分組單元210用于按照偏移地址從小到大查看該大類中的指令,并將以新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度是最短編碼長(zhǎng)度的指令加入到以新的基地址為基地址的組中。
該第二分組單元220用于當(dāng)查看到以新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度不是最短編碼長(zhǎng)度的內(nèi)存訪問(wèn)指令,則以該指令的內(nèi)存地址作為新的基地址創(chuàng)建一個(gè)新的組。
在其中一個(gè)實(shí)施例中,該第二分組單元220可以包括編碼長(zhǎng)度判斷子單元221以及分組子單元222,該編碼長(zhǎng)度判斷子單元221的輸入端與第一分組單元210的輸出端相連接,該分組子單元222的輸入端與該編碼長(zhǎng)度判斷子單元221的輸出端相連接。
其中,該編碼長(zhǎng)度判斷子單元221用于查看是否存在以新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度不是最短編碼長(zhǎng)度的內(nèi)存訪問(wèn)指令。
該分組子單元222用于當(dāng)查看到以新的基地址為基地址時(shí)、指令的編碼長(zhǎng)度不是最短編碼長(zhǎng)度的內(nèi)存訪問(wèn)指令,則嘗試在保證當(dāng)前組的偏移地址的編碼長(zhǎng)度不變的前提下、將當(dāng)前組的基地址增大以使當(dāng)前查看到的內(nèi)存訪問(wèn)指令的偏移地址達(dá)到最短編碼長(zhǎng)度,并將增大后的基地址作為當(dāng)前組的新的基地址,若嘗試不成功,則以該指令的內(nèi)存地址作為新的基地址創(chuàng)建一個(gè)新的組。
在其中一個(gè)實(shí)施例中,該目標(biāo)程序生成模塊500還用于將每一大類的修改基地址的指令插入至目標(biāo)程序的目標(biāo)位置處;其中,目標(biāo)位置為使得每一大類的修改基地址的指令在該大類中所有指令之前被執(zhí)行的位置。
在其中一個(gè)實(shí)施例中,該程序加載模塊100可以包括程序讀取單元110、程序劃分單元120和記錄單元130,該程序劃分單元120的輸入端與程序讀取單元110的輸出端相連接,該記錄單元130的輸入端與程序劃分單元120的輸出端相連接。
其中,該程序讀取單元110用于讀取待編譯的程序。該程序劃分單元120用于將待編譯的程序劃分成若干函數(shù)。該記錄單元130用于記錄每一函數(shù)中每條內(nèi)存訪問(wèn)指令的基地址以及偏移地址。
此處不再贅述關(guān)于編譯器的實(shí)施例,具體可以參見上述編譯方法中所述。
以上所述實(shí)施例的各技術(shù)特征可以進(jìn)行任意的組合,為使描述簡(jiǎn)潔,未對(duì)上述實(shí)施例中的各個(gè)技術(shù)特征所有可能的組合都進(jìn)行描述,然而,只要這些技術(shù)特征的組合不存在矛盾,都應(yīng)當(dāng)認(rèn)為是本說(shuō)明書記載的范圍。
以上所述實(shí)施例僅表達(dá)了本發(fā)明的幾種實(shí)施方式,其描述較為具體和詳細(xì),但并不能因此而理解為對(duì)發(fā)明專利范圍的限制。應(yīng)當(dāng)指出的是,對(duì)于本領(lǐng)域的普通技術(shù)人員來(lái)說(shuō),在不脫離本發(fā)明構(gòu)思的前提下,還可以做出若干變形和改進(jìn),這些都屬于本發(fā)明的保護(hù)范圍。因此,本發(fā)明專利的保護(hù)范圍應(yīng)以所附權(quán)利要求為準(zhǔn)。