專利名稱::用于結(jié)構(gòu)仿真的動態(tài)優(yōu)化目標(biāo)碼翻譯器和翻譯方法
技術(shù)領(lǐng)域:
:本發(fā)明涉及在一主處理系統(tǒng)中運行以仿真第二操作系統(tǒng)的目標(biāo)碼翻譯器技術(shù)。更具體而言,本發(fā)明涉及動態(tài)目標(biāo)碼翻譯器技術(shù),這種翻譯器在一個具有主處理器目標(biāo)碼指令集的主處理器上執(zhí)行過程中實時地完成對原始目標(biāo)碼指令集的分析和計算。在目標(biāo)碼翻譯器領(lǐng)域中,有必要把已在一個計算機上開發(fā)的目標(biāo)碼轉(zhuǎn)換到具有不同計算機結(jié)構(gòu)的另一個計算機上。這種目標(biāo)碼的轉(zhuǎn)換方法包括一種名為“靜態(tài)目標(biāo)碼轉(zhuǎn)換方法”的轉(zhuǎn)換方法,用這種方法時指令語句要首先轉(zhuǎn)換成第二種結(jié)構(gòu)的目標(biāo)碼,然后再執(zhí)行。第二種轉(zhuǎn)換方法是“動態(tài)目標(biāo)碼轉(zhuǎn)換方法”,使用這種方法時第一目標(biāo)碼被轉(zhuǎn)換成第二種目標(biāo)碼而同時執(zhí)行指令。在靜態(tài)目標(biāo)碼轉(zhuǎn)換方法技術(shù)中,執(zhí)行時間不受轉(zhuǎn)換所需時間的影響。然而,在執(zhí)行靜態(tài)目標(biāo)碼轉(zhuǎn)換時,被轉(zhuǎn)換成的目標(biāo)碼的實際大小變大了。換句話說,使用靜態(tài)目標(biāo)碼轉(zhuǎn)換方法時,被轉(zhuǎn)換成的目標(biāo)碼中許多操作步驟不可避免地增大了。結(jié)果,出現(xiàn)了被轉(zhuǎn)換成的目標(biāo)碼性能下降和效率不高的問題。另一方面,使用動態(tài)目標(biāo)碼轉(zhuǎn)換方法時,與靜態(tài)轉(zhuǎn)換成的目標(biāo)碼相比,其轉(zhuǎn)換成的目標(biāo)碼的大小變得比較小。然而,傳統(tǒng)的動態(tài)目標(biāo)碼轉(zhuǎn)換方法有一個問題,即全部目標(biāo),包括很少使用的目標(biāo),都被轉(zhuǎn)換。換句話說,傳統(tǒng)的動態(tài)目標(biāo)碼轉(zhuǎn)換方法不能夠識別出那些被多次執(zhí)行的目標(biāo),從而增加了轉(zhuǎn)換目標(biāo)碼所需時間而犧牲了效率。因此,本發(fā)明的一個目的是提供一種目標(biāo)碼翻譯器,它解決這項技術(shù)中的問題同時提供對翻譯成的目標(biāo)碼的動態(tài)優(yōu)化。本發(fā)明的又一目的是給出主程序的“概要分析(profile)”,直至編譯器完成編譯為止,該概要分析結(jié)果被編譯器用于編譯和優(yōu)化該程序。本發(fā)明的又一目的是在動態(tài)優(yōu)化和編譯過程中從未被翻譯的代碼跳到被翻譯后的代碼。本發(fā)明的又一目的是提供一種帶有軟件反饋的動態(tài)優(yōu)化目標(biāo)碼翻譯器,它計算發(fā)送給編譯器的翻譯請求數(shù)與已完成的翻譯數(shù)之差。本發(fā)明的又一目的是提供從一種機器語言的計算機程序到另一種計算機語言的動態(tài)翻譯而該程序同時在運行過程中。再有,本發(fā)明的一個目的是提供一種動態(tài)目標(biāo)碼翻譯器,它從對應(yīng)于源目標(biāo)碼中多個分支的多個種子中確定要翻譯的段。本發(fā)明的這些目的是由一個計算機結(jié)構(gòu)仿真系統(tǒng)實現(xiàn)的,它在一個目的計算機上仿真一個源計算機,該系統(tǒng)包括一個解釋器用于把源目標(biāo)碼單個地翻譯成相應(yīng)的翻譯后的目標(biāo)碼,并計算源目標(biāo)碼中分支指令的執(zhí)行次數(shù);還包括一個編譯器,用于當(dāng)相應(yīng)的分支指令執(zhí)行次數(shù)超過一閾值時把源目標(biāo)碼指令組合成段,并用于動態(tài)編譯該段。本發(fā)明的這些目的還由一個計算機結(jié)構(gòu)仿真系統(tǒng)實現(xiàn),它在一個目的計算機上仿真一個源計算機,該系統(tǒng)包括多個解釋器用于把源目標(biāo)碼單個地翻譯成相應(yīng)的翻譯后的目標(biāo)碼,這里這多個解釋器的每一個都在執(zhí)行翻譯后的目標(biāo)碼指令的同時實時地概要分析源目標(biāo)碼分支信息;還包括一個編譯器,用于根據(jù)源目標(biāo)碼中相應(yīng)的分支指令把來自這多個解釋器中任何一個的源目標(biāo)碼指令組合成段,并當(dāng)相應(yīng)的分支指令大于一閾值時動態(tài)編譯這些源目標(biāo)碼段。本發(fā)明的又一些目的由一個計算機結(jié)構(gòu)仿真系統(tǒng)實現(xiàn),它在一個目的計算機上仿真一個源計算機,該系統(tǒng)包括一個解釋器用于把源目標(biāo)碼單個地翻譯成相應(yīng)的翻譯后的目標(biāo)碼,這里該解釋器通過存儲每個分支指令的執(zhí)行次數(shù)來記錄源目標(biāo)碼的分支指令,從而使超過閾值的分支指令成為種子;還包括一個編譯器用于根據(jù)這些種子把源目標(biāo)碼指令組合成段并在由解釋器進行翻譯和記錄的過程中動態(tài)編譯這些源目標(biāo)碼段。本發(fā)明的另一些目的由一個多任務(wù)計算機結(jié)構(gòu)仿真系統(tǒng)實現(xiàn),它在一個多任務(wù)目的計算機結(jié)構(gòu)上仿真一源計算機結(jié)構(gòu),該系統(tǒng)包括一個解釋器任務(wù)用于把源目標(biāo)碼單個地翻譯成相應(yīng)的翻譯后的目標(biāo)碼和用于確定源目標(biāo)碼中分支指令的執(zhí)行次數(shù);還包括一個編譯器任務(wù),它在多任務(wù)目的計算機結(jié)構(gòu)上與解釋器一起運行,用于在相應(yīng)的分支指令執(zhí)行次數(shù)超過一閾值次數(shù)時把源目標(biāo)碼組合成段,并用于動態(tài)編譯該段。由下文中結(jié)合附圖對最佳實施例的描述,本發(fā)明的這些和其他目的和優(yōu)點將變得更加明顯和易于理解。這些圖件是圖1是根據(jù)本發(fā)明的一個最佳實施例,一個OOCT系統(tǒng)的高級結(jié)構(gòu)的框圖。圖2是一個流程圖,說明優(yōu)化目標(biāo)碼翻譯的組成部分以及用于編譯一段源代碼的控制流。圖3是一個流程圖,說明在正常執(zhí)行過程中優(yōu)化目標(biāo)碼翻譯的控制流。圖4是一示意圖,說明用于一組變量的OOCT緩沖器。圖5a、5b和5c是說明一翻譯表結(jié)構(gòu)的示意圖。圖6是用于進入和退出一段的解釋器方框圖。圖7是用于創(chuàng)建一段、使該段可被解釋器達到、使老的段不可被達到以及刪除老段的編譯方法方框圖。圖8是說明BRANCH-RECORD(分支記錄)結(jié)構(gòu)的方框圖。圖9是一示意圖,說明一個分支記錄的結(jié)構(gòu),該記錄是存儲BRANCH-RECORD的大散列表的一部分。圖10是一示意圖,說明一個L1高速緩存的結(jié)構(gòu),它是若干BRANCH-RECORD的2維陣列。圖11是一示意圖,說明由一解釋器對L1高速緩存執(zhí)行操作的方法。圖12是一示意圖,說明根據(jù)本發(fā)明一個實施例的編譯器的總體結(jié)構(gòu)。圖13是一示意圖,說明根據(jù)本發(fā)明一個實施例的塊檢出器的舉例。圖14是一代碼輪廓的方框圖,該代碼輪廓帶有兩個外部入口點,這里在ENTRY(入口)指令和GOTO(轉(zhuǎn)向)指令之間插入了填充指令。圖15是說明一個OASSIGN(賦值)插入舉例的方框圖。圖16是一方框圖,說明死代碼刪除和地址檢驗刪除的舉例。圖17是地址檢驗刪除舉例的方框圖。圖18是公共子表達式刪除(“CSE”)舉例的方框圖。圖19是復(fù)制傳播舉例的方框圖。圖20具體說明一常數(shù)合并的舉例。圖21具體說明上述過程的舉例,它有一個根據(jù)本發(fā)明一實施例的比較基礎(chǔ)結(jié)構(gòu)。圖22具體說明對于有不同相鄰指令的同一指令產(chǎn)生代碼的舉例。圖23說明根據(jù)本發(fā)明第二實施例用于動態(tài)優(yōu)化目標(biāo)碼翻譯的系統(tǒng)配置。圖24說明根據(jù)本發(fā)明第三實施例用于并發(fā)動態(tài)翻譯的系統(tǒng)配置。圖25說明根據(jù)本發(fā)明第三實施例把解釋器和編譯器組合(例如在執(zhí)行過程中組合成一個任務(wù))和把它們分開(例如分成不同的任務(wù))二者之間的差別。圖26說明根據(jù)本發(fā)明第四實施例的翻譯表用于記錄哪些指令是可翻譯的而哪些指令是不可翻譯的。圖27說明根據(jù)本發(fā)明第四實施例該方法如何減小仿真器上進行概要分析的負(fù)擔(dān)。圖28說明根據(jù)本發(fā)明第五實施例具有單獨解釋器和編譯器的動態(tài)翻譯系統(tǒng)的總體結(jié)構(gòu)圖。圖29說明根據(jù)本發(fā)明第五實施例的軟件反饋機制的組成部分。圖30說明根據(jù)本發(fā)明第六實施例在翻譯任務(wù)忙時如何使用一隊列來保持翻譯請求。圖31說明根據(jù)本發(fā)明第六實施例如何由OOCT請求隊列把便宜的共享存儲器請求與系統(tǒng)調(diào)用請求組合在一起。圖32顯示根據(jù)本發(fā)明第七實施例一個動態(tài)翻譯器如何會容易引起頁面錯誤,而這種錯誤在源指令的正常執(zhí)行過程中是不會發(fā)生的。圖33顯示根據(jù)本發(fā)明第七實施例用于從頁面錯誤中恢復(fù)和繼續(xù)進行翻譯的算法。圖34說明根據(jù)本發(fā)明第八實施例在常有分支概要分析器(profiler)的動態(tài)翻譯系統(tǒng)中的控制流圖象。圖35說明根據(jù)本發(fā)明第九實施例該動態(tài)翻譯器如何使用分支概要分析信息去計算一個基本塊的執(zhí)行概率。現(xiàn)在將詳細參考本發(fā)明的最佳實施例,在附圖中說明了它們的舉例,這里相似的參考數(shù)字始終代表相似的部件。本發(fā)明第一實施例I.系統(tǒng)概述一般而言,本發(fā)明涉及一種優(yōu)化目標(biāo)碼翻譯器(下文中稱作“OOCT”),它作為一個計算機結(jié)構(gòu)仿真系統(tǒng)的一部分,完成一個微處理器指令集的動態(tài)編譯。編譯是動態(tài)的,因為在運行時之前沒有對應(yīng)用指令集的任何簡單的訪問。相對于基于模板翻譯和基于模板的解釋而言,使用一編譯器作為目標(biāo)碼翻譯系統(tǒng)一部分可允許系統(tǒng)完成分析的優(yōu)化,從而改善仿真的性能。仿真用主處理器最好是市場上可得到的處理器,如英特爾奔騰(IntelPentium)處理器。奔騰處理器指令集的體系結(jié)構(gòu)有利于管理不同大小的數(shù)據(jù),從而有利于仿真16位和8位兩種目標(biāo)碼指令。16位和8位目標(biāo)碼指令可以是為第二處理器上的原始應(yīng)用設(shè)計的,例如來自富士通的K系列處理器。只有知道指令流程圖才可能完成有意義的編譯器類型優(yōu)化。在傳統(tǒng)的編譯器中,流程圖是給出的而且是定義得很好的。因為在開始優(yōu)化之前整個程序例程被完全地語法分析過。對于OOCT,則不是這種情況。在運行該程序之前,指令在存儲器圖象中的位置是未知的。這是因為指令長度是可變的,并帶有任意插入的非指令數(shù)據(jù)集。指令的位置如同所有到指令中的結(jié)合點的位置一樣,都是未知的。所以,為確定流程圖,該程序必須被運行。一個解釋器第一次運行該程序。在解釋器執(zhí)行該程序時,解釋器每當(dāng)完成一個分支操作時便通知OOCT。這一信息記錄標(biāo)識出一些指令和一些結(jié)合點。隨著該程序的運行,關(guān)于流程圖的信息變得更完全,盡管永遠不會全部完全。OOCT被設(shè)計成借助關(guān)于流程圖的部分信息進行工作優(yōu)化是根據(jù)可能不完全的流程圖進行的,而且該系統(tǒng)被設(shè)計成允許當(dāng)能得到更多信息時替換被優(yōu)化的代碼。動態(tài)編譯根據(jù)解釋器收集的概要分析信息選擇對哪些部分內(nèi)容進行優(yōu)化。當(dāng)某一分支被執(zhí)行的次數(shù)超過一個閾值數(shù)時,那個分支的目的地變?yōu)榫幾g的種子。這個種子是對要被編譯的一部分K指令作為一個單元進行語法分析的起始點。一個段所包含的主處理器指令是從種子開始對原處理器指令進行優(yōu)化的結(jié)果。一個段作為一個單元被安裝或卸下。當(dāng)解釋器調(diào)用OOCT以通告有一個分支時,如果用于目的地的代碼存在的話,OOCT可能選擇把控制轉(zhuǎn)交給該段。類似地,該段可能包含代碼用于把控制轉(zhuǎn)交回解釋器。一個段本身可能是不完全的,這樣該段只代表原始程序的可能流程路徑的一個子集。但這種不完全的表達不影響該仿真的正確操作。如果發(fā)生了新的未預(yù)料到的穿過原代碼的流程路徑,那么控制流程將跳回解釋器。然后,這同一段能被替換,以考慮新的控制流程。II.OOCT代碼結(jié)構(gòu)根據(jù)本發(fā)明的一個實施例,OOCT可以在傳統(tǒng)的操作系統(tǒng)環(huán)境,如Windows(視窗)下運行。然而,根據(jù)本發(fā)明的第二實施例,OOCT可以被構(gòu)成為與第二操作系統(tǒng)的仿真固件鏈接,如富士通的KOI操作系統(tǒng)。III.體系結(jié)構(gòu)圖1說明OOCT系統(tǒng)100的高級體系結(jié)構(gòu)。圖1顯示兩個任務(wù),即解釋器110和編譯器104。解釋器110和編譯器104在一多任務(wù)操作系統(tǒng)下同時操作。這兩個任務(wù)即能借助分支記錄器112訪問分支記錄,又能訪問編譯過的代碼段108。此外,解釋器110能向編譯器104發(fā)送編譯請求。在下文中的通信部分中將給出這兩個任務(wù)之間通信的更完全描述。編譯流程控制圖2說明OOCT100的主要組成部分以及編譯一部分原始代碼的控制流程。主要OOCT級如下。首先,解釋器110通過與分支記錄器112通信記錄下分支信息。然后分支記錄器112使用種子選擇方法確定把哪個種子發(fā)送到編譯器104。然后,塊檢出器114使用種子和分支概要分析信息選擇一段原始代碼以進行編譯。然后該塊檢出器114建立一個描述待編譯原始指令的控制流程圖(CFG),并把CFG傳送給塊布局(layout)單元116。然后塊布局單元116把控制流程圖展平為線性的指令列表。優(yōu)化代碼產(chǎn)生單元118完成從原始指令到翻譯后的代碼段指令的實際編譯。所產(chǎn)生的翻譯后代碼與關(guān)于被編譯代碼段的信息一起最后被傳送給段安裝單元120,它使這些代碼對解釋器110可用。OOCT執(zhí)行控制流程圖3說明在正常執(zhí)行過程中OOCT中的控制流程。在解釋器110執(zhí)行代碼過程中,當(dāng)執(zhí)行某些指令時,OOCT能進入分支記錄器112。分支記錄器能返回解釋器110,或者若該分支的目的已被編譯則進入被安裝的編譯后代碼段之一。由被編譯的代碼,能逐段進行翻譯,或回到解釋器110。編譯后代碼或者能調(diào)用解釋器110去執(zhí)行單條原始指令,或者能跳到解釋器110,把所有控制轉(zhuǎn)交給解釋器110。對主體應(yīng)用的第一實施例的描述可分解如下。第一部分描述解釋器110和編譯器104之間的接口。第二部分描述為了OOCT而對解釋器110所做的修改。第三部分描述編譯器104。最后一部分描述一個視窗測試環(huán)境。在第一實施例描述之后將描述本發(fā)明第二至第九實施例。IV通信(公共單元)解釋器110和編譯器104彼此以多種方式通信。解釋器110通過與分支記錄器112通信,把分支信息記錄到分支日志中。編譯器104也能讀這個分支日志。編譯器104創(chuàng)建編譯后代碼段并把它們的入口點存入翻譯表,解釋器110讀此表。解釋器110還通過一緩沖器向編譯器104發(fā)送種子地址。由編譯器104和解釋器110兩者用于這種通信的源代碼是在公共單元中。這一部分描述這種通信如何進行。共享OOCT緩沖器編譯器104和解釋器110之間的所有通信都直接通過OOCT緩沖器,它是一個大的共享存儲區(qū)。某些通信還利用系統(tǒng)調(diào)用從解釋器110向編譯器104發(fā)送消息以及反向發(fā)送。下面列出的表1顯示了OOCT緩沖器的靜態(tài)分配部分圖。緩沖器的其余部分動態(tài)分配給不同的數(shù)據(jù)結(jié)構(gòu),如下面給出的表2所示。在OOCT緩沖器的靜態(tài)分配部分中的某些字段(field)指向動態(tài)分配部分中的數(shù)據(jù)結(jié)構(gòu)。這些指針有上標(biāo)號顯示它們指向什么。例如,在靜態(tài)分配部分中的分區(qū)字段有標(biāo)號2,而且該分區(qū)字段指向動態(tài)分配部分中的分區(qū)存儲器,該分區(qū)存儲器也有標(biāo)號2。表1.OOCT緩沖器的靜態(tài)分配部分>在OOCT緩沖器的動態(tài)分配部分,數(shù)據(jù)結(jié)構(gòu)的大小取決于若干變量。一個變量是原始處理器(如富士通的ASP)的操作系統(tǒng)所用系統(tǒng)頁面數(shù)。對于含有要被翻譯的指令的ASP地址空間的每個頁面,在翻譯表中有一個翻譯過的頁。另一個參數(shù)是該系統(tǒng)預(yù)期記錄的分支指令數(shù)。它當(dāng)前預(yù)期220個分支,這影響B(tài)RANCH_RECORD(分支記錄)數(shù)組和分支頭段表二者的大小。解釋器110數(shù)影響L1級分支記錄器高速緩存的大小,因為對每個任務(wù)有一個高速緩存。圖4是對于一組變量其OOCT緩沖器的構(gòu)成圖。在圖4中,ASP頁面數(shù)是10MB個ASP指令,解釋器110個數(shù)是4,OOCT緩沖器的總大小是128MB。表2OOCT緩沖器的動態(tài)分配部分分支日志(分支記錄器112)分支日志數(shù)據(jù)結(jié)構(gòu)是BRANCH_RECORED(分支記錄)數(shù)組、分支標(biāo)題表和分支L1級高速緩存。對于分支記錄器112如何工作的解釋,請看下文中的解釋器修改部分。這一部分將描述分支日志怎樣用于從解釋器110向編譯器104傳送信息。圖4說明初始化后的OOCT緩沖器。各區(qū)域的大小是按比例尺畫出的。對于這一實例,OOCT緩沖器的大小是128MB,ASP頁面數(shù)是2560,解釋器110的個數(shù)是2,預(yù)期分支指令數(shù)是220。編譯器104讀取分支日志,以發(fā)現(xiàn)有多少次條件分支指令被采用,有多少次條件分支指令未被采用。編譯器104以兩種方式使用這一信息。第一,當(dāng)編譯器104語法分析指令時,編譯器104試圖只分析那些已被最頻繁執(zhí)行的指令。如果出現(xiàn)了一個條件分支指令,它檢驗有多少次是分支了,有多少次它未能通過。第二,當(dāng)編譯器104產(chǎn)生代碼時,編譯器試圖把一條件分支指令的最可能的后繼指令放在緊跟該分支指令的后面。這使生成的代碼運行得更快。為了辯別哪個指令更可能是后繼指令,編譯器104使用分支日志信息。請參考下文提出的編譯器104信息集,以得到更多細節(jié)。BRANCH_Get_Record(ooct/compiler/branch.c)當(dāng)編譯器104想要讀分支日志信息時,它以分支指令地址調(diào)用過程BRANCH_Get_Record(得到分支記錄)。該過程在分支日志中尋找該分支,并返回指向BRANCH_RECORD(分支記錄)數(shù)組的元素之一的指針。編譯器104于是能看到該分支曾執(zhí)行過多少次,有多少次它分支了,有多少次它沒能通過。翻譯表(TRANSUNIT(單元))翻譯表含有關(guān)于ASP地址空間中每條指令的信息。該翻譯表記錄該指令是否是一個分支的目的地(JOIN(聯(lián)結(jié))),該指令是否被作為一個種子發(fā)送到編譯器104(BUFFERED(被緩存))以及是否存在該段的編譯后代碼入口點(ENTRY(入口))。當(dāng)OOCT被初始化時,該翻譯表是空的。當(dāng)分支指令被記錄時,它們的目的地被標(biāo)記為JOIN(聯(lián)結(jié))點。如果該分支執(zhí)行次數(shù)多于閾值,則該目的地將被作為種子發(fā)送給編譯器104并且翻譯表入口將被標(biāo)注為BUFFERED(被緩存)。在編譯器104結(jié)束了對翻譯后版本的編譯之后,它把入口點的地址存入該翻譯表,并把它們標(biāo)注為ENTRY(入口)。圖5a、5b和5c說明根據(jù)本發(fā)明的一個最佳實施例的翻譯表結(jié)構(gòu)。如圖5a中所示,一個ASP地址被分成兩部分。高20位是頁面號,低12位是頁面偏移。圖5b表明,頁面號被用作進入第一級翻譯表的索引。ASP作用的頁面在第一級表中。ASP未使用的頁面沒有指針,因為決不會有任何指令帶有那個頁面號。這些指針指向第二級翻譯表。把頁面偏移加到該指針上便給出一個翻譯表的入口。如圖5c所示,每個入口條目是32位長,它的各字段示于其底部。第一位說明該ASP指令是否是一個聯(lián)結(jié)點。第二位說明對該指令是否有一個段入口點。第三位說明該指令是否曾作為種子發(fā)送給編譯器104。如果對該指令存在入口點的話,則該翻譯表入口條目的其余各位是該入口點地址,如果沒有入口點的話則為0。由于K機器結(jié)構(gòu)有可變長指令,所以翻譯表對每個ASP地址有一個入口條目,包括在指令地址及數(shù)據(jù)地址中部的地址。翻譯表的構(gòu)造示于圖5a、5b和5c中。如前所提,第二級翻譯表對每個ASP地址有一個32位的入口條目。所以,如果ASP使用10MB空間,則第二級翻譯表使用40MB。有若干過程和宏讀和寫翻譯表的入口條目。TRANS_Set_Entry_Flag(ooct/common/trcommon.h)TRANS_Set_Entry_Flag(翻譯設(shè)置入口條目標(biāo)志)宏啟動翻譯表入口條目的標(biāo)志JOIN(聯(lián)結(jié))、ENTRY(入口)或BUFFERED(被緩存)之一。它使用帶有鎖前綴的匯編語言指令,從而使它自動設(shè)置該位。TRANS_Reset_Entry_Flag(ooct/common/trcommon.h)TRANS_Reset_Entry_Flag(翻譯復(fù)位入口條目標(biāo)志)宏關(guān)掉翻譯表入口條目的JOIN(聯(lián)結(jié))、ENTRY(入口)或BUFFERED(被緩存)標(biāo)志之一。它使用帶有鎖前綴的匯編語言指令,從而使它自動復(fù)位該位。TRANS_Entry_FlagP(ooct/common/trcommon.h)TRANS_Entry_FlagP(翻譯入口條目標(biāo)志P)宏讀和返回翻譯表入口條目的標(biāo)志JOIN(聯(lián)結(jié))、ENTRY(入口)或BUFFERED(被緩存)之一的狀態(tài)。TRANS_Test_And_Set_Entry_Flag(ooct/common/trcommon.h)TRANS_Test_And_Set_Entry_Flag(翻譯測試和設(shè)置入口條目標(biāo)志)過程自動讀標(biāo)志JOIN(聯(lián)結(jié))、ENTRY(入口)或BUFFERED(被緩存)之一的狀態(tài),如果它尚未被啟動則把它啟動。它返回調(diào)用該過程之前該標(biāo)志的狀態(tài)。TRANS_Set_Entry_Address(ooct/common/trcommon.h)TRANS_Set_Entry_Address(翻譯設(shè)置入口點地址)過程寫入翻譯表入口條目的入口點地址。它使用帶有鎖前綴的匯編語言指令,從而使它自動寫此地址。請注意,如果沒有段鎖,則入口點地址是一目標(biāo)指令的地址,如果存在段鎖,則它是SEGMENT_GATE(段門)數(shù)據(jù)結(jié)構(gòu)的地址。TRANS_Get_Entry_Address(ooct/common/trcommon.h)TRANS_Get_Entry_Address(翻譯取入口點地址)過程讀和返回翻譯表入口條目的入口點地址。請注意,如果沒有段鎖,則一入口點地址是一目標(biāo)指令的地址,但如果有段鎖,則它是一SEGMENT_GATE(段門)數(shù)據(jù)結(jié)構(gòu)的地址。段段是可由KOI系統(tǒng)執(zhí)行的一個編譯后代碼單元。下文中提供的編譯器104材料描述如何創(chuàng)建和刪除一個段。這一部分描述編譯器104如何告知解釋器110關(guān)于一段的信息,解釋器110如何進入和退出該段,以及編譯器如何告知解釋器去停止使用一段和切換到另一段。當(dāng)一個段被創(chuàng)建時,存在若干個ASP指令地址,在那些地址解釋器110能進入此段。對這些地址的每一個,編譯器104創(chuàng)建一個到該段的入口點。一個入口點是該段中的一個特殊點,解釋器110被允許轉(zhuǎn)跳到那里。在該段的其他點,編譯后代碼假定某些值在寄存器中,所以轉(zhuǎn)跳到那里是不安全的。為告知解釋器110這些入口點在哪里,編譯器104為每個第n次TRANS_Get_Entry_Address(翻譯取入口點地址)調(diào)用TRANS_Set_Entry_Address(翻譯設(shè)置入口點地址)。當(dāng)編譯后代碼段進入分支記錄器112時,解釋器110檢驗這些編譯后代碼段。它們調(diào)用TRANS_Entry_FlagP(翻譯入口條目標(biāo)志P)看當(dāng)前ASP地址是否有一入口點。如果有,則它們調(diào)用TRANS_Get_Entry_Address(翻譯取入口點地址)去讀該地址。如果段鎖被設(shè)置,則它們鎖住該段(見下文),然后跳到該入口點。如果段鎖未被設(shè)置,則它們只是跳到該入口點。編譯后代碼決定何時它應(yīng)退出。通常,當(dāng)它需要執(zhí)行的一條指令不是這同一段的一部分時發(fā)生這種情況,于是它轉(zhuǎn)跳到解釋器110。編譯器104能刪除一個編譯后代碼段,并告知解釋器110去使用另一段。編譯器104這樣做的作法是取消翻譯表入口條目的ENTRY(入口)位,改變?nèi)肟邳c地址,然后再次設(shè)置ENTRY(入口)位。段鎖段鎖是OOCT系統(tǒng)的一個可選特性。隨著系統(tǒng)運行過程中分支記錄器積累更多的信息,編譯器104能產(chǎn)生一段的新版本,它優(yōu)于那老的一個。段鎖允許編譯器104用新的段替換老的段,并重新聲明被老段使用的存儲器??上У氖牵捂i使分支記錄器112和編譯后代碼變慢了。所以在執(zhí)行OOCT代碼的時間和它所用空間之間有一個權(quán)衡。這一部分描述段鎖如何工作。段鎖代碼有兩部分。第一部分是用于除實現(xiàn)段鎖外的OOCT系統(tǒng)所有部分的接口。這一接口保證一段只能處于四個很好定義的狀態(tài)之一,而且將以很好定義的方式自動改變狀態(tài)。第二部分是段鎖本身的實現(xiàn),它滿足由該接口做出的保證。設(shè)計圖3中給出一個段可能處于的狀態(tài)。一個段可以是可達到的或者是不可達到的,以及它可以是被鎖住的或未被鎖住的。當(dāng)在翻譯表中有一個或多個入口點時,這些段是可達到的。當(dāng)在翻譯表中沒有到該段的入口點時,該段是不可達到的。一個入口點是一個結(jié)構(gòu),它含有一個鎖和一個指令地址。該鎖能被不只一個解釋器110同時使用,它對有多少解釋器110使用該入口點及包含該入口點的段進行計數(shù)。當(dāng)一段的一個或多個入口點被鎖住時,該段即被鎖住。當(dāng)它的所有入口點都未被鎖住時,它即未被鎖住。如果一段是不可達到的和未被鎖住的,則編譯器104可以重新聲明和刪除該段。每個段在編譯器104創(chuàng)建它時開始處于U/U狀態(tài)。當(dāng)編譯器104把它的入口點寫入翻譯表時它進入到R/U狀態(tài)。當(dāng)一解釋器110進入和退離該段時,它能進入R/L狀態(tài)和返回R/U狀態(tài)。編譯器可以創(chuàng)建一個新段,它與老段翻譯的指令相同。在這種情況下,它將重寫翻譯表中老的段入口點,這使老段成為不可達到的。當(dāng)編譯器104重寫該段最后一個入口條目時,如果一個解釋器110正在使用它的話,它從R/L狀態(tài)進入U/L狀態(tài),或者,如果沒有解釋器110在使用它,則它從R/U狀態(tài)進入U/U狀態(tài)。最終,使用該段的所有解釋器110將釋放它們的鎖,于是該段將處于U/U狀態(tài)。于是編譯器104能重新聲明和刪除它,因為沒有任何解釋器110在使用它,而且沒有任何一個能進入它。表3.一個段能處于的狀態(tài)圖6顯示根據(jù)本發(fā)明的一具實施例解釋器110進入和退離一段122的情況。圖中央的段122是由編譯器104產(chǎn)生的代碼單元。當(dāng)由解釋器110使用該段時,它必須在所有時候都被鎖住。因此,在進入段122之前一個鎖計數(shù)器(未畫出)被增量,而在退離段122之后該鎖計數(shù)器被減量。由于解釋器110不能自動地查尋入口點和鎖住該入口點,所以必須確定在被鎖住后該入口點沒有改變。圖7顯示編譯器104創(chuàng)建一段、使該段可由解釋器110達到、使老的段不可達到、以及刪掉老段的方法。在步驟S200,編譯器104創(chuàng)建一新段并把相關(guān)的入口點加入翻譯表。當(dāng)在步驟S200中添加一個入口點時,一個老的入口點可能被重寫。這老的入口點現(xiàn)在是不可達到的了,于是可以被重新使用,如果沒有任務(wù)(如解釋器110或編譯器104)持有對它的鎖的話。該老入口點被放到一個重新聲明列表(未畫出)上。步驟S202說明編譯器104如何使用重新聲明列表。步驟S202檢驗一個入口點是否被鎖住。如果該入口點未被鎖住,那么該入口點未被任何解釋器110使用,所以能從擁有它的段中移走。然而,如果該段再沒有任何入口點,那么該段未被一任務(wù)(如解釋器110和編譯器104)使用,而且沒有任務(wù)能進入它。所以,該段能被刪掉。段鎖接口允許OOCT的大部分能忽略同步的細節(jié),因為一個段總是看起來處于很好定義的狀態(tài),而且所有狀態(tài)過渡看起來都是自動發(fā)生的。然而,在段鎖代碼內(nèi)部這種轉(zhuǎn)換不是自動的,因為Intel目的地在硬件中不支持這種復(fù)雜的操作。所以,段鎖代碼使這種轉(zhuǎn)換看起來是自動的。實現(xiàn)執(zhí)行解釋器110和編譯器104所用的過程分別示于圖6和圖7。這兩個過程合作以保證每個轉(zhuǎn)換看起來是自動的。在下文描述中的編號參考是指圖6和圖7而言的。在段接口的四個狀態(tài)當(dāng)中有六種可能的轉(zhuǎn)換,它們歸于四組。第一個轉(zhuǎn)換是U/U到R/U,這是當(dāng)編譯器104把段入口點寫入翻譯表(*6)從而使該段成為可達到的時候出現(xiàn)的。因為編譯器104是允許寫翻譯表的唯一任務(wù),所以不需要同步化來使這一轉(zhuǎn)換自動化。第二組轉(zhuǎn)換是R/U到U/U及類似的從R/L到U/L轉(zhuǎn)換。當(dāng)編譯器104把一段的最后的入口點重寫入翻譯表(*306)時便發(fā)生這些轉(zhuǎn)換。雖然編譯器104能自動地把一新入口點寫入翻譯表,但解釋器110不能自動讀和鎖住一個入口點(*301,*302)。解釋器110得要在一個操作中讀該入口點并在另一操作中鎖住它。如果解釋器110從翻譯表中讀一個老入口點,然后編譯器104寫一個新入口點,然后解釋器110鎖住這老的入口點,這時會暴露出一個潛在的問題。在這種情況下,編譯器104假定入口點是不可達到的,但解釋器110能進入該段,這是一個錯誤。為防止這一問題,解釋器110驗證翻譯表在鎖住后包含同一入口點(*303)。如果翻譯表包含這同一入口點,那么它仍是可達到的,而且進入該段是安全的。如果翻譯表不包含這同一入口,則解釋器110必須釋放它的鎖并且不進入該段。第三組轉(zhuǎn)換是R/U到R/L及其逆轉(zhuǎn)換R/L到R/U。這第一種情況發(fā)生于一解釋器110從翻譯表中讀出入口點并將其鎖住(*302)之時。第二種情況發(fā)生于該解釋器110在其退出點離開一段(*304)和進入解鎖過程(*305)之時,鎖住和解鎖指令本身不在該段中是重要的,因為任何時候該段被解鎖,編譯器104都可能刪掉它(*3011)。第四種轉(zhuǎn)換是從U/L到U/U。它也發(fā)生于解釋器110退離一段(*304)和進入解鎖過程(*305)之時。在這一轉(zhuǎn)換發(fā)生之后,該段被解鎖,而且編譯器104將通過兩種測試(*309,*3010)并刪掉該段(*3011)。由于解釋器110能保持鎖住一段達任意長時間,所以讓編譯器104等待一個鎖是低效率的。因此,編譯器104不試圖以鎖住入口點來防止解釋器110使用它們。其替代作法是它只是使該段成為不可達到的,其后再檢驗鎖是否已被釋放(*309)。一旦鎖被釋放,該入口點能成為自由入口點并被重新使用。監(jiān)視消息隊列解釋器110向編譯器104發(fā)送種子地址。它們使用兩個消息隊列來發(fā)送種子地址。第一個使用KOI系統(tǒng)調(diào)用ScMsgSnd和ScMsgRcv來發(fā)送和接收種子。第二個隊列使用OOCT緩沖器中的一個共享存儲區(qū)。這共享區(qū)叫做branch_seed_Buffer(分支種子緩沖器)。使用兩個隊列的理由是每個隊列有一個優(yōu)點和一個缺點。解釋器110去使用KOI系統(tǒng)調(diào)用是代價昂貴的,所以不應(yīng)很頻繁地使用。然而,KOI系統(tǒng)調(diào)用允許在沒有種子要編譯時使編譯器104鎖住。這允許KOI系統(tǒng)使用編譯器104的CPU去做某些其他工作。共享存儲緩沖器的優(yōu)點在于它對解釋器110而言很便宜,而它的缺點在于當(dāng)沒有種子時編譯器104不能鎖住。通過使用兩個隊列,OOCT獲得這兩種方法的優(yōu)點。當(dāng)編譯器104空閑時,它調(diào)用ScMsgRcv去鎖住。在這種情況中,解釋器110以ScMsgSnd調(diào)用發(fā)送下一個種子以喚醒編譯器104。當(dāng)編譯器104工作時,解釋器110通過branch_seed_Buffer(分支種子緩沖器)區(qū)發(fā)送種子,它比較快些。在編譯器104結(jié)束一次編譯時,它檢驗sch_Seed_Buffer(分支種子緩沖器)區(qū)。如果存在任何種子的話,那么它編譯它們。當(dāng)它結(jié)束以所有種子的編譯時,它再次調(diào)用ScMsgRcv并鎖住。V.解釋器修改(執(zhí)行單元)OOCT的設(shè)計包括對解釋器110的三類修改。第一,OOCT需要由解釋器110初始化。第二,解釋器110已被修改成使用分支記錄。最后,解釋器110已被修改成允許向編譯后代碼轉(zhuǎn)換和由編譯后代碼轉(zhuǎn)換回來。本報告將詳細描述這些修改。OOCT解釋器代碼能在兩種方式下運行,這兩種方式是OOCT_ERFORMANCE_MODE(OOCT運行方式)和OOCT_DEBUG_MODE(OOCT調(diào)試方式)。本報告描述OOCT_PERFORMANCE_MODE(OOCT運行方式)的所有特性,并指出OOCT_DEBUG_MODE(OOCT調(diào)試方式)的不同之處。初始化在OOCT編譯任何代碼或記錄任何分支之前,解釋器110調(diào)用OOCT_INIT(OOCT初始化)來初始化OOCT數(shù)據(jù)結(jié)構(gòu)。OOCT_INIT(OOCT初始化)以及它調(diào)用的過程完成下列步驟。初始化翻譯表。MCD指令告知OOCT在系統(tǒng)地址空間中的頁面。過程TRANS_Execution_Init(翻譯執(zhí)行初始化)創(chuàng)造第一級翻譯表,從而使系統(tǒng)頁面的入口條目指向第二級翻譯表數(shù)組。這些數(shù)組在初始化時被充零。請參見通信部分以了解關(guān)于翻譯表的細節(jié)。初始化分支記錄器112。過程BRANCH_Execution_Init(分支執(zhí)行初始化)為若干數(shù)據(jù)結(jié)構(gòu)初始化OOCT_buffer(OOCT緩沖器)中的存儲器。第一,有一個分支日志本身,它含有關(guān)于分支指令的概要信息。第二,有一個第一組(L1)高速緩存,它使分支記錄器112操作快些。第三,有一個種子緩沖器,它包含從分支記錄器112向編譯器104發(fā)送的種子。第四,有若干個由編譯后代碼調(diào)用的全局函數(shù)。在BRANCH_Execution_Init(分支執(zhí)行初始化)過程中這些全局函數(shù)的地址被存儲在OOCT_buffer(OOCT緩沖器)中。關(guān)于分支日志和第一級高速緩存的更多信息,請參見上文中關(guān)于分支記錄器112的部分。分配編譯器104的堆棧存儲器。編譯器104使用一個被分配到OOCT_buffer(OOCT緩沖器)中的特殊大堆棧。1.分配編譯器104的區(qū)存儲器。在編譯過程中編譯器104使用這一存儲器,該存儲器在OOCT_buffer(OOCT緩沖器)中。2.分配編譯后段存儲器。編譯后代碼被放在OOCT_buffer(OOCT緩沖器)的這一區(qū)域中。3.對統(tǒng)計信息充零。當(dāng)OOCT被初始化時,在OOCT統(tǒng)計區(qū)域中的絕大部分信息被復(fù)位。分支記錄器與解釋器的接口當(dāng)解釋器110執(zhí)行系統(tǒng)代碼中的一個分支指令而且OOCT方式位已被設(shè)置時,解釋器110通過下述例行程序之一來調(diào)用分支記錄器112這四個例行程序檢驗編譯后代碼入口點以找到目的地地址,如果該入口點存在,則轉(zhuǎn)跳到該入口點。如果它不存在,則例行程序調(diào)用branch_L1_Touch(分支L1級接觸)(見下一部分)以更新分支日志,然后轉(zhuǎn)跳到解釋器110的“提取”例行程序。更新分支日志表圖8顯示根據(jù)本發(fā)明一個實施例的BRANCH_RECORD(分支記錄)的結(jié)構(gòu)。分支記錄代碼對于一個分支已執(zhí)行了多少次進行計數(shù)。有兩個數(shù)據(jù)結(jié)構(gòu)由分支記錄器112用于存儲這計數(shù)。第一,分支日志,它由多處理器系統(tǒng)中所有仿真處理器共享。第二,系統(tǒng)中每個仿真處理器有一個一級(L1)高速緩存。分支執(zhí)行計數(shù)首先被寫入高速緩存,然后寫入分支日志。這一部分描述L1高速緩存和分支日志的結(jié)構(gòu)。它還描述分支記錄器112如何使用它們。關(guān)于每個分支的信息存儲在稱作BRANCH_RECORD(分支記錄)的結(jié)構(gòu)中。它包括該分支地址、該分支目的地、跟隨該分支的失敗指令、該分支已執(zhí)行的大約次數(shù)、以及該分支已被采取過的大約次數(shù)。BRANCH_RECORD(分支記錄)的最后一個字段是指向另一個BRANCH_RECORD(分支記錄)的指針。它用于在一個鏈接表中連接各BRANCH_RECORD(分支記錄)。散列表被組織成一個鏈接列表數(shù)組。圖9顯示分支日志的結(jié)構(gòu)。它是一個存儲各BRANCH_RECORD(分支記錄)的大散列表。每個解釋器110有其可變local_branchheader_table(本地分支頭段表)的自己的付本,但它們都指向OOCT緩存區(qū)中的同一數(shù)組。local_branch_header_table(本地分支頭段表)的元素是指向BRANCH_RECORD(分支記錄)列表的指針。查尋一BRANCH_RECORD(分支記錄)以找到一個分支的過程有三個步驟。1.散列目的地地址。(index=BRANCH_HASH)(destination_address)%BRANCH_TABLE_SIZE.)(索引=分支散列(目的地地址)%分支表大小)。2.得到列表頭段。(list=local_branch_header_table[index].)(列表=本地分支頭段表[索引]。)3.沿著該列表走下去直至你發(fā)現(xiàn)一條記錄具有這同一分支地址。(while(list>branch_address?。絙ranch_address)list=list->next.)圖9具體說明可變的local_branch_header_table(本地分支頭段表)是一個指向各列表的指針數(shù)組。每個列表含有具有相同目的地地址的那些BRANCH_RECORD(分支記錄)。當(dāng)沒有列表時,在local_branch_header_table(本地分支頭段表)中的那個指針是NULL(空)。分支日志含有關(guān)于分支的全部信息,但它有兩個問題。第一,查尋和插入BRANCH_RECORD(分支記錄)是慢操作。它們太慢了,以致不能在每次解釋器110記錄一個分支時都進行這兩種操作。第二,每個解釋器110使用這同一個分支日志。為了保持BRANCH_RECORD(分支記錄)列表的一致性,在一個時刻只有一個Exec(解釋器)能訪問該分支日志,這使多處理器系統(tǒng)減慢,甚至比單處理器系統(tǒng)減慢得還厲害。為了修復(fù)這些問題,對每個解釋器110有一個L1級高速緩存。L1級高速緩存能被快速訪問,而且解釋器110能并行訪問它們的L1級高速緩存。每個L1級高速緩存是一個BRANCH_L1_RECORD(分支L1記錄)結(jié)構(gòu)的二維數(shù)組。該數(shù)組的基地址存于變量branch_L1_table(分支L1表)中。圖10顯示L1級高速緩存的結(jié)構(gòu)。該高速緩存是BRANCH_L1_RECORD(分支L1記錄)的二維數(shù)組。第一維是BRANCH_L1_SETS(分支L1集合)(當(dāng)前為32),第二維是BRANCH_L1_SETSIZE(分支L1集合大小)(當(dāng)前為4)。數(shù)組的每行是一個集合。同一分支指令總是使用高速緩存的同一集合,但它能位于不同的位置。如圖10中所示,L1高速緩存被組織成集合。對于一個分支,其集合號等于(branch_address+branch_destination)%BRANCH_L1_SETS。該集合的4個成員保持具有同一集合號的4個最新近的分支。這被稱作4路集合結(jié)合性(associativity)。當(dāng)幾乎在同一時刻執(zhí)行具有相同集合號的若干分支時,這種4路集合結(jié)合性改善了高速緩存的性能。圖11說明根據(jù)本發(fā)明的一個實施例由解釋器110執(zhí)行L1級高速緩存操作的一種方法。換句話說,圖11說明使用L1級高速緩存的分支記錄方法。優(yōu)化目標(biāo)碼翻譯方法利用兩種存儲器形式來記錄未編譯的分支,即1.一個分支日志,其動態(tài)變化的大小與被記錄的分支數(shù)成比例,以及2.一個分支高速緩存,名為L1級高速緩存,其中有限個數(shù)未被編譯的被記錄分支按增強訪問的順序存儲。分支日志和L1級高速緩存代表由操作系統(tǒng)管理的虛擬存儲器位置。這樣,術(shù)語“L1級高速緩存”是任意給予存儲未編譯分支的高速緩存的,不應(yīng)與通常在處理器(例如PentiumPro(奔騰處理器))上發(fā)現(xiàn)的L1級高速緩存混淆。根據(jù)本發(fā)明的優(yōu)化目標(biāo)碼翻譯器提供的解釋器110可以調(diào)用多個不同的分支記錄例行程序。然而,每個分支記錄例行程序本身調(diào)用一個子例程,它決定是轉(zhuǎn)跳到編譯后代碼還是記錄一個分支指令。這一子例程具體顯示在圖11中。鑒于上述,為執(zhí)行具有L1級高速緩存的分支記錄方法,該方法首先從步驟S400開始。在步驟S401中,解釋器110首先檢驗該分支目的地的編譯后代碼入口點(即所針對的段先前是否被編譯過)。如果有一個入口點,即“是”,則存在一個編譯后的段,于是流程跳到S402以立即執(zhí)行編譯后代碼段。然后執(zhí)行編譯后代碼段直至達到結(jié)束標(biāo)志,于是流程返回以執(zhí)行下一段。當(dāng)然,該段未被記錄在分支日志中,因為該分支被編譯過了。如果在步驟S401中沒有入口點,即“否”,于是沒有對應(yīng)于分支指令的編譯后代碼。于是流程轉(zhuǎn)向步驟S404,解釋器110觀察L1級高速級存以確定該分支與存儲在L1級高速緩存中的多個分支之間是否有可能的匹配。步驟S404確定該分支與存儲在L1級高速緩存中的多個分支之間是否存在一個匹配。L1級高速緩存被分成多個集合,每個集合由一個唯一的集合號表示。根據(jù)本發(fā)明的一個實施例,每個集合含有4個分支。步驟S404首先確定一個高速緩存集合號“S”,它對應(yīng)于當(dāng)前的分支地址,其S=(branch_address+branch_destination)%BRANCH_L1_SETS。接下來,針對當(dāng)前分支地址和目的地,順序檢驗branch_L1_table[S]的每個元素。如果檢測到一個匹配,即“是”,則流程進入步驟S406,字段“encountered_sub_count”(一個字段,它指出該分支已被遇到過多少次)和“taken_sub_count”(一個字段,它指出該分支被采取過多少次)被更新。然后流程進入S407。在步驟S407,確定當(dāng)前分支地址已被遇到的次數(shù)是否大于一個預(yù)先確定的閾值數(shù)。最佳閾值是在1000次的數(shù)量級。這樣,在步驟S407中,字段encountered_sub_count與該閾值比較。如果該閾值被超過,即“是”,則流程進入步驟S410,于是對這一分支高速緩存的信息被寫回分支日志。另一方面,如果該閾值未被超過,即“否”,則流程進入步驟S412。步驟S412是當(dāng)前子例程的一個終點,它跳到IC_FETCHO2,即解釋器110的入口點。如果當(dāng)前分支不在高速緩存中,即在步驟S404中為“否”,于是流程進入步驟S408,于是在前面由“S”指定的集合中的一個BRANCH_L1_RECORD記錄(即含有所有可能被更新字段,如encountered_sub_count和taken_sub_count,的記錄)被從L1高速緩存中刪除并寫到分支日志中。接下來,當(dāng)前分支信息被寫入由“S”指定的集合中。再有,在把當(dāng)前分支記錄寫入集合“S”的過程中,當(dāng)前分支記錄作為該集合的第一元素放置。這是因為這同一分支很可能被再次執(zhí)行,從而提高了系統(tǒng)的性能和效率。換句話說,步驟S404將被執(zhí)行得更快些。即使當(dāng)該分支在高速緩存中,即“是”,如果自它最后一次被泄出后已被執(zhí)行過多次,那么它可以被拷貝到分支日志中。當(dāng)使用L1級高速緩存時,步驟序列幾乎總是S400,S404,S406,S407和S412。因此,本發(fā)明試圖使那些步驟盡可能快。當(dāng)把當(dāng)前分支信息放在該集合的第一元素中時,該分支信息使步驟S404變快,因為解釋器110可能再次執(zhí)行這同一分支。上面提出的分支記錄方法通過執(zhí)行先前已被編譯的代碼和增強對尚未達到編譯閾值水平但經(jīng)常被調(diào)用的分支指令的訪問能力,從而減輕了處理器的負(fù)擔(dān)。在這方面,OOCT的主要目的是使步驟S400幾乎每次都取“是”分支。如果一個分支被頻繁地執(zhí)行,那么對于它的目的地應(yīng)有編譯后的代碼段。第二個目標(biāo)是使跟隨步驟S401的“否”路徑更快些,從而使當(dāng)未被編譯的分支將不會顯著地減慢程序的執(zhí)行?!胺瘛甭窂降淖盥糠直环Q作“瀉出(flush)”。在步驟S408和S410中,分支信息被從L1級高速緩存中“瀉出”并寫入分支日志。為了把一種子發(fā)送到編譯器,把分支信息瀉出變?yōu)楸匾?,這將使得產(chǎn)生編譯后代碼并使步驟S400在未來對這一分支回答“是”。然而,并不是每次執(zhí)行一個未被編譯分支地址時都必須瀉出分支信息。對于每100次執(zhí)行或少于100次執(zhí)行進行一次瀉出往往是可行的。所以,本發(fā)明試圖提高不包括瀉出的步驟S400、S404、S406、S407及S412的速度。這樣,總是取較快的路徑,除非發(fā)生兩件事之一。在步驟S404,有可能在該集合中未發(fā)現(xiàn)分支信息,于是我們?nèi)〉絊408的“否”路徑。在步驟S407,如果分支被執(zhí)行次數(shù)超過“閾值”次數(shù),它將取“是”路徑到S410,S410也包括一個瀉出。在OOCT_DEBUG_MODE(OOCT調(diào)試方式)中,仍然使用L1級高速緩存方法,但瀉出該高速緩存的閾值設(shè)為l,于是每次分支執(zhí)行時其信息被寫入分支日志。種子選擇當(dāng)一個分支指令被很頻繁地執(zhí)行時,分支記錄器112把它的目的地地址發(fā)送給編譯器104。這一地址被稱作“種子”,而且選擇種子是OOCT系統(tǒng)的很重要部分。種子應(yīng)是在一過程開頭或一循環(huán)頭處的地址。所以,分支記錄器112只發(fā)送作為無條件分支目的地的那些種子。種子應(yīng)是頻繁被執(zhí)行的地址,所以只有當(dāng)一個分支的encountered_count(遇到次數(shù))字段大于一閾值時該分支目的地才成為一個種子。該閾值存于OOCT緩沖器中名為“seed_production_threshold(種子產(chǎn)生閾值)的字段中。該閾值能隨時改變,在下一部分中將描述它。閾值設(shè)置關(guān)于使用固定的閾值來確定是否發(fā)送一個種子,有兩件不好的事情。第一,當(dāng)編譯器104空閑時,該閾值可能太高。在這種情況中,對于編譯器104而言,有有用的工作去做,但分支記錄器112沒有告知編譯器104去做什么。第二,當(dāng)消息隊列滿時,該閾值可能太低。在這種情況中,分支記錄器112將試圖把一種子發(fā)送出去,即使該種子將不會添入隊列中,這是浪費時間。幸好,有可能檢測這兩種情況,即何時編譯器104空閑及何時消息隊列滿,并改變該閾值。分支記錄器112在過程branch_Update_Entry(分支更新入口)中通過讀名為num_monitor_seed_messages的OOCT緩存區(qū)字段。以檢測編譯器104是否空閑。如果該字段為0,則表明編譯器104已經(jīng)結(jié)束了被發(fā)送來的全部種子。該閾值太高,于是分支記錄器112降低它。當(dāng)分支記錄器112試圖發(fā)送一個種子而得到一個出錯代碼指出該消息未被發(fā)出時,它在過程branch_Send_Seed(分支發(fā)送種子)檢測到一個滿的消息隊列。該閾值太低,于是分支記錄器112提高它。在OOCT_DEBUG_MODE(OOCT調(diào)試方式),該閾值從不改變。在這種情況中它的值被設(shè)置到過程OOCT_INIT(OOCT初始化)的第三變元中。處理多任務(wù)OOCT在具有多個解釋器110的多任務(wù)系統(tǒng)中運行。這些任務(wù)有各自的分支L1級高速緩存,但它們使用同一個分支日志表。當(dāng)分支信息從L1級高速緩存瀉出到分支日志表時,解釋器110獲得一個關(guān)于該表的記錄,從而使它不與任何其他解釋器(Exec)沖突。有兩種方式處置對分支日志鎖的關(guān)注。第一個是使解釋器110等待直至該鎖可以得到,然后得到該鎖并寫它的分支信息。這使解釋器110運行得更慢,但使分支日志更準(zhǔn)確。第二個是如果解釋器110不能得到鎖,則放棄,不去寫分支信息。這種方式使解釋器110更快些,但失掉了某些分支記錄信息。OOCT使用第二種方式,因為解釋器110的速度比分支日志的準(zhǔn)確性更重要。為了使系統(tǒng)功能發(fā)揮得好,分支日志信息只需近似正確。當(dāng)OOCT以多個解釋器110運行時,任務(wù)之一是特殊主控任務(wù),它調(diào)用OOCT_INIT去初始化OOCT緩沖器和分支記錄數(shù)據(jù)結(jié)構(gòu)。其他任務(wù)是從屬任務(wù),它們只需初始化某些局部變量和它們的L1級高速緩存。在主控任務(wù)已結(jié)束初始化OOCT_buffer(OOCT緩沖器)之后,從屬任務(wù)調(diào)用Slave_OOCT_Init(從屬OOCT初始化)。主控任務(wù)和從屬任務(wù)之間的同步使用下述方法。主控任務(wù)方法1.執(zhí)行MCD指令啟動OOCT。2.調(diào)用OOCT_INIT,它初始化OOCT緩沖器和分支記錄數(shù)據(jù)結(jié)構(gòu)。3.喚醒從屬任務(wù)。4.轉(zhuǎn)跳到解釋器。從屬任務(wù)方法1.進入睡眠狀態(tài)。當(dāng)主控任務(wù)執(zhí)行時(上述步驟3)醒來。2.調(diào)用Slave_OOCT_Init,它初始化該任務(wù)的單獨分支L1級高速緩存。3.轉(zhuǎn)跳到解釋器。用戶/系統(tǒng)空間轉(zhuǎn)換OOCT系統(tǒng)只編譯來自ASP地址空間系統(tǒng)頁面的指令。它忽略用戶頁面。解釋器110單獨區(qū)的OOCTSTS位控制分支記錄器112是否被調(diào)用。這一位主要由兩個宏NEXT_TO和NEXT_OUN來控制。然而,有一種情況,其中OOCT不得不設(shè)置這一位。當(dāng)編譯后代碼段以非固定分支指令結(jié)束時,它可能使PSW-IA從系統(tǒng)空間移動到用戶空間,這需要把OOCTSTS置于0。于是,一個以非固定分支結(jié)束的編譯后代碼段跳到例行程序branch_Exit_Log(分支退出記錄)它檢驗?zāi)康牡氐刂泛驼_設(shè)置OOCTSTS位。編譯后代碼接口向/從編譯后代碼轉(zhuǎn)換當(dāng)解釋器110調(diào)用一個分支記錄例行程序并為該分支目的地找到一個編譯后代碼段時,解釋器110把執(zhí)行傳送給編譯后代碼(見圖11)。當(dāng)段鎖被斷掉時,解釋器110直接跳到該入口點。當(dāng)段鎖被啟動時,解釋器必須在跳到入口點之前試圖鎖住該段。如果它鎖住該段,則它跳到該入口點。如果它未能鎖住該段,則它跳回解釋器110。有幾種方式使執(zhí)行退離編譯后代碼段,它們在表4中描述。在所有情況中,當(dāng)控制跳回解釋器110時,ESI和EDI寄存器有正確值,而解釋器110的單獨區(qū)有完好的K狀態(tài)。表4.如何控制退離一編譯后代碼段當(dāng)段鎖被啟動時,解釋器110將在執(zhí)行那個代碼時保持對該編譯后代碼段的鎖住。在它退離該段后它必須釋放這鎖,于是編譯后代碼調(diào)用分支記錄器112中的某些過程,這些過程釋放該鎖,然后跳到解釋器110。中斷在編譯后代碼執(zhí)行過程中能發(fā)生若干中斷,如IO中斷或MCHK中斷。編譯后代碼檢驗其單獨區(qū)的INTINF字段以檢測是否已發(fā)生了中斷。它檢測在任何可能的無限循環(huán)內(nèi)部的這一字段,這保證它不會永遠忽略該中斷。如果一個中斷確已發(fā)生,則編譯后代碼調(diào)用解釋器110的帶有完好K狀態(tài)的例行程序IU_OINTCHK。它預(yù)期解釋器110將返回編譯后代碼。解釋器回調(diào)某些K操作碼不被OOCT翻譯。代之以編譯后代碼調(diào)用解釋器110子例行程序IC_OOCT去解釋該操作碼然后返回編譯后代碼。編譯后代碼確保在調(diào)用IC_OOCT之前ESI和EDI寄存器有正確值而且單獨區(qū)有完好的K狀態(tài)。如果解釋器110在執(zhí)行IC_OOCT子例行程序過程中檢測到錯誤,它調(diào)用OOCT_EXCP而且不返回到編譯后代碼。如果段鎖被啟動,則OOCT_EXCP釋放段鎖。例外當(dāng)編譯后操作碼有一無屏蔽例外時,如一操作例外或零除數(shù)例外,則編譯后代碼調(diào)用一解釋器子例行程序IC_PGMxx,這里xx是01h和21h之間的出錯代碼號。解釋器110試圖處置這一例外并返回。當(dāng)解釋器110不能返回時,它調(diào)用OOCT_EXCP,它釋放任何段鎖。使用全局函數(shù)某些K操作碼,如字符處理操作,翻譯成大量的目標(biāo)操作碼。對這些操作碼作多重翻譯將會占用太多的段存儲器…,稱作全局函數(shù)的子例行程序由編譯后代碼調(diào)用以執(zhí)行這些操作碼。這些全局函數(shù)恰如同執(zhí)行K操作碼的解釋器110例行程序,只是它們被專門設(shè)計成由編譯后代碼調(diào)用和返回編譯后代碼。有用于5個操作碼即SBE、CC、MV、TS和C的全局函數(shù)。實驗表明,全局函數(shù)比調(diào)用解釋器110的IC_OOCT入口點快得多,而且它們使用的存儲器比多次把操作碼編譯成目標(biāo)指令所使用的存儲器小得多。VI.編譯器概述在鉆研編譯的細節(jié)之前,重要的是在高一級理解編譯器104的主要目的和理解編譯器104的結(jié)構(gòu)。編譯器104的目的是把當(dāng)前執(zhí)行程序中多次執(zhí)行的部分翻譯成優(yōu)化目標(biāo)碼并使解釋器110能得到這些代碼去執(zhí)行。圖12具體說明編譯器104的總體結(jié)構(gòu)。編譯器104接收來自分支記錄器112(前面討論過)的種子,它們啟動編譯過程。種子是一個原始指令的地址,它已是當(dāng)前執(zhí)行程序中大量分支的目標(biāo)。這是要給出一個起始點,以找出當(dāng)前執(zhí)行程序中的頻繁執(zhí)行部分。塊檢出器114利用這個種子以及由分支記錄器112提供的其他信息去檢出程序中應(yīng)被編譯的部分。一旦已選出了要被編譯的原始代碼,則要經(jīng)過三個主要階段。第一階段是把K操作碼轉(zhuǎn)換成被編譯器104的其余部分使用的中間語言(IL)。中間語言是由IL發(fā)生器124產(chǎn)生的。第二階段是由前以描述并稱作優(yōu)化器126的優(yōu)化方法完成對IL的各種分析和優(yōu)化變換。最后階段是把IC轉(zhuǎn)換成可重新分配地址的機器代碼,這一階段被命名為優(yōu)化代碼產(chǎn)生單元118。編譯器104的最后一項工作是使優(yōu)化的代碼能被解釋器110使用。于是,借助段安裝單元以優(yōu)化代碼付本創(chuàng)建一個段數(shù)據(jù)結(jié)構(gòu)。然后該段被安裝在OOCT緩沖器(未畫出)內(nèi)的一個共享區(qū)中。最后,翻譯表被更新,從而使解釋器110到編譯后K代碼的任何分支將使用新的目標(biāo)代碼代替。這一部分的其余內(nèi)容將詳細討論上述編譯器104各個階段。在這部分的末尾還將討論許多其他實施細節(jié)。塊檢出編譯器104接收一單個種子地址以開始編譯。在種子處開始,它讀取原始指令,其至已經(jīng)讀到如過程體之類的東西。然后,它把這一組原始指令傳送到編譯器104的下一階段,即產(chǎn)生IL階段。由編譯器104讀出的指令單元被稱作基本塊,所以這一階段被稱作塊檢出器,即塊檢出器114。一個基本塊是一個指令序列,在那里控制只能在第一條指令處進入,而且只能在最后一條指令處退出。這意味著只有第一條指令能是一個分支的目標(biāo),而且只有這最后一條指令能是分支指令。這還意味著如果該塊的第一條指令被執(zhí)行,那么所有指令將被執(zhí)行。塊檢出器圖13顯示根據(jù)本發(fā)明一實施例的塊檢出器114的一個實例。過程OOCT_ParseFrom實現(xiàn)塊檢出器114。它一次讀一個基本塊。由于5個理由之一而結(jié)束一個基本塊。1.如果分析器讀到一個分支指令,則該塊以此分支結(jié)束。2.如果下一條指令曾已經(jīng)分析過,則該塊以當(dāng)前指令結(jié)束,因為每個K操作碼在一段中只應(yīng)出現(xiàn)一次。3.如果下一條指令是一結(jié)合點,則該塊以當(dāng)前指令結(jié)束,因為結(jié)合點得要在一基本塊的開始。4.如果當(dāng)前指令是一個因子“on(啟動)”,而且它能后跟數(shù)據(jù)而不是指令,則該塊以當(dāng)前指令結(jié)束。5.如果當(dāng)前指令是一個非法指令,則該塊以當(dāng)前指令結(jié)束。在讀每塊之后,塊檢出器114決定下一步采取什么行動,這要根據(jù)該塊的結(jié)束方式??赡艿男袆恿杏诒?。表5讀一塊后的行動一個舉例示于圖13。塊檢出器114在種子指令處開始,它是一條LB指令。由于它不是一個分支或結(jié)束指令因子,所以它繼續(xù)下一條指令。那是一條TH指令,它是一個條件分支。由于該條件指令,塊檢出器114停止讀當(dāng)前塊。它在LH和LF指令處繼續(xù)讀新塊。當(dāng)它讀SVC指令時,塊檢出器114結(jié)束那塊,因為SVC是結(jié)束指令的一個因子。當(dāng)它讀GO指令時,塊檢出器114結(jié)束那塊,因為GO是一個分支指令。在L8指令處它繼續(xù)讀,因為L8指令是一個分支目的地。在讀ST8指令之后,塊檢出器114結(jié)束該塊,因為它已經(jīng)讀過下一條指令。對于塊檢出器114將讀的指令數(shù)有一個上限。這一限制的目的是防止編譯器104在編譯源指令過程中運行超出存儲器。這一限制由OOCT_trace.c(OOCT跟蹤)中的常數(shù)MAX_KINST_NUM(最大K指令數(shù))設(shè)置,當(dāng)前值為500。當(dāng)塊檢出器114試圖讀一指令時,它能引起頁面錯。如果塊檢出器114得到一個頁面錯,它停止讀當(dāng)前塊,但它從尚未試過的任何分支目的地開始繼續(xù)讀。這允許編譯器104創(chuàng)建一個段,即使它不能分析一個種子能達到的全部指令。塊布局在選擇基本塊之后,塊檢出器調(diào)用過程OOCT_GenerateIL(OOCT產(chǎn)生IL)以創(chuàng)建IL指令,編譯器104的其余部分將使用它們。在此時,有可能重新安排塊的順序。這叫做塊布局,這有助于編譯器104為PentiumPro(奔騰處理器)處理器產(chǎn)生更好的代碼,因為如果不采取向前的條件分支的話,PentiumPro(奔騰處理器)會運行得更快。考慮圖13中的例子。它有一個條件分支,即TH指令。在原始指令中,歸于結(jié)束的基本塊是以LH開始的一個塊,而目的地塊是以LF開始的一個塊。如果在75%的時間里該條件分支被采取,那么如果LF基本塊放在歸于結(jié)束的位置而LH基本塊放在采取分支的位置,則運行得要較快。OOCT_GenerateIL過程根據(jù)分支日志中的信息對各塊進行布局。只要可能,它便把條件分支的最通用后繼者放在歸于結(jié)束的位置。這一過程產(chǎn)生一個IL指令列表,它們被傳送到編譯器104的優(yōu)化階段。中間語言(IL)的產(chǎn)生這一部分將討論產(chǎn)生代表K操作碼的編譯器104中間語言(IL)的過程。在直接討論如何產(chǎn)生IL之前,先給出IL概述并描述對理解有重要性的數(shù)據(jù)結(jié)構(gòu)。IL概述編譯器104的主要分析和翻譯過程(pass)是在一種中間語言上操作的,這種中間語言是一種不依賴機器的指令集。由于兩個主要理由,使用中間語言是一種標(biāo)準(zhǔn)的編譯器104技術(shù)。第一,IL通常有一種結(jié)構(gòu)體系,它簡化了分析和變換。第二,IL允許許多不同的源語言使用相同的優(yōu)化和代碼產(chǎn)生階段并易于重新指向不同的平臺。由OOCT使用的IL(從這里起只稱作IL)當(dāng)前由表6所列40個不同的操作碼組成。這些指令歸于三類。第一,如ADD和LOAD等功能性操作碼,它們有到標(biāo)準(zhǔn)機器操作碼的直接映射。第三,如LABEL和CGOTO等管理控制流程的操作碼。最后,有多個特殊操作碼,它們被編譯器104用作為特殊的標(biāo)記,它們不直接對應(yīng)于由后端產(chǎn)生的代碼。這些特殊的標(biāo)記操作碼在另一部分中描述。由于IL代表一個虛似機器,所以如果需要更多的功能,則只要對IL增加其他操作碼。IL由指令組成,每條指令指定一個操作碼,一個類型,和若干個偽寄存器變元。由編譯器104支持的類型是有符號和無符號8位、16位及32位值。除了由SET操作碼使用的立即值和由LOAD操作碼從存儲器加載的值外,所有變元都由偽寄存器傳遞。偽寄存器就是IL虛擬機器寄存器。編譯器104允許任意個數(shù)偽寄存器,其中每個有一預(yù)先定義的大小(例如16位)。每個偽寄存器直接對應(yīng)于一個具體的存儲器位置。對于OOCT,這些存儲器位置是在單獨區(qū)的OOCT特定部分之中。偽寄存器到存儲器位置的這種映射給出兩個好處。第一,它使IL成一流線,不需要把公用值加載到臨時位置和把它們存回存儲器的IL操作。第二,編譯器104往往能把公用值分配到機器寄存器,從而消除了多余的加載和存儲。表6IL操作碼特殊IL操作碼OOCTIL包含少量具有特殊目的的操作碼。大多數(shù)IL操作碼對應(yīng)于由后端產(chǎn)生的代碼。不同的是,這些特殊指令的作用是向編譯器104發(fā)出的信號,說明有某種特殊事情正在發(fā)生。IL含有如下特殊操作碼ENTRY,SYNC,EXTMOD以及OASSIGN。這部分討論這些操作碼中的頭三個。OASSIGN已在前文中充分說明了。ENTRY操作碼標(biāo)記出在那里控制能進入流程圖的一點。由OOCT產(chǎn)生的代碼可以有多個外部入口點,它們代表外部聯(lián)結(jié)點。每個外部入口有一個相應(yīng)的ENTRYIL指令。ENTRY指令發(fā)生在代碼結(jié)尾,而且立即跟隨一個GOTO指令,它跳到代碼主體內(nèi)的一個標(biāo)號處。使用一個入口代替外部入口直接跳到該標(biāo)號的理由是允許代碼發(fā)生器在ENTRY和轉(zhuǎn)跳到該標(biāo)號之間插入內(nèi)容。圖14是具有兩個外部入口點的代碼輪廓,這里在ENTRY指令和GOTO指令之間插入了填充指令。換句話說,圖14具體說明了根據(jù)本發(fā)明一個實施例的一個入口舉例。SYNC操作碼用于保證一組偽寄存器被瀉入存儲器。具體地說,OOCT使用SYNC操作碼來保證所有K寄存器處于解釋器110預(yù)期找到它們的那些存儲器位置。SYNC的作用如同對寄存器分配器的一個指示,指出一個處于已被修改的機器寄存器中的偽寄存器被瀉掉。SYNC的作用還在于使用任何活的數(shù)據(jù),它使編譯器104免于以死的代碼刪除只具有修改K寄存器作用的代碼。EXTMOD操作碼用于指出偽寄存器被修改,但編譯器104沒有關(guān)于該寄存器已被如何修改的細節(jié)。這樣,EXTMOD有兩個作用。第一,它對優(yōu)化過程,如常數(shù)合并或副本傳播,起到阻擋層的作用。第二,它迫使編譯器104的寄存器分配器在下次死用偽寄存器之前插入一個填充。在OOCT中,EXTMOD指令用于回調(diào)解釋器104之后,以指出哪些K寄存器可能已被修改。IL數(shù)據(jù)結(jié)構(gòu)在討論如何從K操作碼構(gòu)建IL之前,熟悉一下編譯器104中所用主要數(shù)據(jù)結(jié)構(gòu)是有用處的。ZONE(compiler/zone.[h,c])在編譯器104中的存儲器分配是由一個叫做ZONE(分區(qū))的抽象(abstraction)來處理的。ZONE抽象是分配存儲器的一種有效方式,以致它能立即釋放全部所分配的存儲器。使用ZONE抽象,分配是快速的,而且程序員不必?fù)?dān)心存儲器泄漏,因為對ZONE的破壞將會重新聲明所用的全部存儲器。在編譯器104中,一個ZONE創(chuàng)建了,于是分配存儲器的所有調(diào)用(即通常會是malloc(存儲器分配)調(diào)用)調(diào)用帶有初始創(chuàng)建的ZONE的ZONE_Alloc(區(qū)分配)。當(dāng)編譯器104完成時,它調(diào)用ZONE_Destroy(區(qū)破壞)它把整個ZONE免去分配(即等效于釋放全部存儲器)。ZONE的底層實現(xiàn)使用大塊存儲器。例如,當(dāng)創(chuàng)建ZONE時,它可能‘malloc’(存儲器分配)大小為0×2000字節(jié)的一塊存儲器。在這一大塊存儲器用完之前,對ZONE_Alloc的調(diào)用將使用這個‘大塊’存儲器。當(dāng)初始-0×2000字節(jié)中沒有空間服務(wù)于ZONE_Alloc請求時,便創(chuàng)建一個新的‘大塊’存儲器。再有ZONE_Alloc調(diào)用時,將使用這個‘大塊’,直到它也被用完為止。在編譯器104中,由于存儲器全都是預(yù)先分配的,所以情況稍有些復(fù)雜,這樣便不能使用malloc。代替作法是使用了一個特殊的ZONE分配器單元(ZALLOC單元)。這ZONE分配器以一個大的存儲器空間(例如0×10000字節(jié))來初始化。它把存儲器分成同樣大小的大塊,ZONE將使用這些大塊進行分配,它還保持一個空余大塊列表。這樣,‘malloc’請求由對ZALLOC_get_chunk的調(diào)用替代,它給回一個空余的‘大塊’存儲器。類似地,在ZONE_Destroy中對‘free(釋放)’的調(diào)用由對ZALLOC_free_chunk的調(diào)用替代。在當(dāng)前的實現(xiàn)中,能由ZONE_Alloc處置的最大分配區(qū)是初始大塊的大小。把ZALLOC改變成處置可變大小的分配以代替簡單地處置一個大小,則會‘修補’這種限制(這類分配器的一例可參見段分配單元)。這里沒有這樣做的理由有兩個。第一,可變大小的分配器復(fù)雜得多,而且會引起破碎等問題。第二,這大塊的大小可做得很大而幾乎不付出什么代價。當(dāng)大塊的大小足夠大時,如果編譯器104已經(jīng)運行超出存儲器,那它也將只是請求了一次大于這大塊大小的分配而已。這樣,要產(chǎn)生ZALLOC單元去處理可變大小的分配并沒有真正的好處。IL_CTXT(compiler/oc_common/include/il_internal.h)編譯器104維持一個單一數(shù)據(jù)結(jié)構(gòu)IL_CTXT來跟蹤編譯的當(dāng)前狀態(tài)。IL_CTXT數(shù)據(jù)結(jié)構(gòu)存儲一指針指向一個被鏈接的IL_NODE列表,這些IL_NODE代表當(dāng)前被編譯的代碼。IL_CTXT還存儲許多其他字段,它們用于整個編譯過程,如所使用的ZONE和IL_FRAM。編譯器104的每個階段有IL_CTXT作為變元并且對這個數(shù)據(jù)結(jié)構(gòu)進行修改,例如許多階段要添加或刪除某些IL_NODE。IL_NODE(compiler/oc_common/include/il_internal.h)IL_NODE數(shù)據(jù)結(jié)構(gòu)代表編譯器104中間語言中單一的一個作為從K操作碼翻譯過來的抽象指令。那些IL_NODE從K操作碼翻譯而來,被保持在一個雙鏈接列表中。指向該列表中第一和最后元素的指針保持在IL_CTXT中。這個列表代表當(dāng)前被編譯器104作用的代碼。編譯器104的每次掃描都要遍歷這一列表,或者產(chǎn)生關(guān)于該列表中代碼的信息,或者變換這一列表。每個IL_NODE含有一個操作字段‘op’,它指出該指令的基本性質(zhì)。它還含有一個操作數(shù)字段矢量,代表該指令的各操作數(shù)。對操作數(shù)字段的解釋取決于該指令的操作類型。除了操作和操作數(shù)外,所有IL_NODE含有許多字段由所有節(jié)點(node)類型共享,例如該指令的Kpc,該指令就是從它翻譯過來的,還有為該代碼產(chǎn)生的目標(biāo)機器碼的首地址等。根據(jù)操作類型,在一個節(jié)點中的操作數(shù)字段個數(shù)是不同的。事實上,在某些情況下,同一類的兩個節(jié)點可能有不同數(shù)量的操作數(shù);例如,一個調(diào)用操作的操作數(shù)個數(shù)將取決于傳送給目標(biāo)方法的變元個數(shù)。操作數(shù)個數(shù)的這種變化表明IL_NODE的大小不是一致的,而且操作數(shù)矢量是IL_NODE結(jié)構(gòu)中的最后一項。操作數(shù)矢量被聲明為一個入口條目長,各IL_NODE的地址分配是通過計算/分配為公共字段和操作數(shù)字段所必須的存儲總量和通過把所分配的存儲器加到一個IL_NODE指針來實現(xiàn)的。在大多數(shù)情況中,但不是在所有情況中,每個操作數(shù)實際上需要操作數(shù)矢量中兩個相繼的入口條目。在偽寄存器中將找到操作數(shù),該偽寄存器的入口條目是operand[i]。如果該操作數(shù)是一目的地操作數(shù),操作數(shù)[i+1]將指向一個節(jié)點列表,該列表使用由這一操作定義的值;如果該操作數(shù)是一個源操作數(shù),則操作數(shù)[i+1]將指向含有該值定義的一個節(jié)點列表。如果一個操作有一目的地操作數(shù),該操作數(shù)將總是存儲在operand和operand[1]中。如果operand[i]是一個源(輸入或使用)操作數(shù),則operand[i+2]也將是一個源操作數(shù);就是說,所有的源寄存器必須在操作數(shù)列表的末尾。在一個節(jié)點中的操作數(shù)字段從來不會被直接訪問。相反,是由一大組形為ILOP_xxx(N)的宏進行訪問的,這里N是指向一個IL_NODE的指針。這些宏知道對于所有各種指令類型各種操作數(shù)在操作數(shù)矢量中是如何存儲的。一些節(jié)點類型如下(該列表沒有包括全部)一元操作這些代表各種簡單的一元(1個源操作數(shù))指令,包括賦值。type(類型)操作的類型ILOP_DEST(N)目的地寄存器;結(jié)果進到這里ILOP_DEST_use(N)使用該目的地寄存器的指令列表ILOP_SRC(N)源寄存器ILOP_SRC_def(N)定義該源的指令列表二元操作這一類代表大量的二元(2個源操作數(shù))指令。type(類型)該操作的類型ILOP_DEST(N)目的地寄存器;結(jié)果進到這里ILOP_DEST_use(N)使用該目的地寄存器的指令列表ILOP_SRC1(N)第一源寄存器ILOP_SRC1_def(N)定義該第一源的指令列表ILOP_SRC2(N)第二源寄存器ILOP_SRC2_def(N)定義該第二源的指令列表ILOP_DIVEX(N)這一操作數(shù)只出現(xiàn)于DIV和REM操作,并指向一個(單獨)列表,該列表含有的節(jié)點代表“被零除例外”的起始,如果存在這個例外的話。LABEL一個LABEL(標(biāo)號)指令代表節(jié)點中的能由分支轉(zhuǎn)到的一點。它包含如下操作數(shù)ILOP_LABEL(N)標(biāo)識該標(biāo)號的唯一整數(shù)ILOP_LABEL_refs(N)指向這一標(biāo)號的指令的列表ILOP_LABEL_live(N)顯示出哪些寄存器在這一標(biāo)號處有效的一個BITSET(位組)ILOP_LABEL_rd(N)達到這一標(biāo)號的每個寄存器的定義列表矢量ILOP_LABEL_misc(N)任何掃描過程暫放關(guān)于該標(biāo)號的私用信息的地方GOTO一個GOTO指令代表到一個標(biāo)號的無條件分支。ILOP_LABEL(N)標(biāo)識目標(biāo)標(biāo)號的唯一整數(shù)ILOP_LABEL_refs(N)目標(biāo)LABEL(標(biāo)號)指令的單獨列表CGOTO一個CGOTO指令代表到一個標(biāo)號的條件分支。它含有與GOTO指令相同的操作數(shù)以及某些附加操作數(shù)。ILOP_COND(N)含有分支條件的寄存器。這個寄存器必須含有一個布爾(B1)型值。如果這條件為TRUE(真),則該分支將被采取。ILOP_COND_def(N)定義這個寄存器的指令的列表ILOP_COND_live(N)顯示如果該分支未被采取的話哪些寄存器有效的BITSET(位組)。除了指定指令的ILOP宏以外,還有許多能在任何指令上使用的類屬宏。ILOP_HasDEST如果該指令有一目的地寄存器,則返回TRUE(真)。在這種情況中,對這一指令可以使用ILOP_DEST和ILOP_DEST_use兩個宏。IL_OP_START,IL_OP_DONE,IL_OP_NEXT用于通過一指令各源寄存器的重復(fù)。IL_OP_START返回一個IL_OP_INDEX指出第一個這種源寄存器。IL_OP_DONE測試一個IL_OP_INDEX看它是否指向了一個源寄存器;如果它沿有,則返回TRUE(真)。IL_OP_NEXT用于繼續(xù)到下一個源寄存器。IL_OP,IL_OP_def這些對一給定IL_OP_INDEX返回具體的源寄存器及其定義列表。這五個宏一般用于如下形式的循環(huán)中for(op=IL_OP_START(n);!IL_OP_DONE(n,op);op=IL_OP_NEXT(n,op)){useIL_OP(n,IL_FRAME(compiler/oc_common/include/il_frame.h,compiler/OOCT_Frame.c)IL_FRAME數(shù)據(jù)結(jié)構(gòu)用于給出關(guān)于編譯后代碼將在其中運行的上下文。該框架定義每個偽寄存器的大小和存儲器位置,這些偽寄存器與其他偽寄存器怎樣重疊,以及在該分配器中使用哪些機器寄存器是合法的。此外,IL_FRAME結(jié)構(gòu)規(guī)定對于正被編譯的代碼是否需要一個C堆??蚣?。在OOCT中不使用C堆??蚣?。在編譯器104中,IL_FRAME結(jié)構(gòu)由OOCT_Frame.c中的函數(shù)初始化。這些函數(shù)建立對應(yīng)于K寄存器和PSW位置的每個偽寄存器。此外,還設(shè)置編譯器104的臨時偽寄存器,它們對應(yīng)于解釋器110的工作空間區(qū)。還建立了關(guān)于K寄存器如何重疊的信息。NL_LIST(compiler/oc_common/[include,src]/nl_nodelist.h)在許多地方編譯器104使用IL_NODE列表,NL_LIST數(shù)據(jù)結(jié)構(gòu)提供了管理這些節(jié)點列表的一個抽象。例如,前文提出的UseDef分析創(chuàng)建使用給定定義的IL_NODE列表以及可能是為給定用途下定義的IL_NODE列表。NL_LIST抽象是直接的,它提供了創(chuàng)建、添加、刪除、替換、查尋和重復(fù)節(jié)點列表的能力。K操作碼到IL的翻譯在前文提出的塊檢出器114已選擇了要編譯的那些K操作碼之后,從K操作碼到IL的翻譯涉及3個主要步驟。第一步是確定為基本塊產(chǎn)生的代碼的順序。塊布局方法已在前文中提出。第二,由于K操作碼基本塊已由塊布局方法選擇,這些操作碼被檢驗以確定它們能否組合成邏輯操作碼。最后,根據(jù)K操作碼及其變元,調(diào)用IL產(chǎn)生過程。OpcodeCombination(操作碼組合)(compiler/ooct_opcode_combine.c)。某些K操作碼序列能被描述成單個邏輯操作碼。例如,已經(jīng)確定,兩個TR指令構(gòu)成的一個序列曾用于測試一個32位寄存器對之值,作法是測試每個單獨的一半。這兩個TR指令代表一個在K結(jié)構(gòu)中得不到的32位測試邏輯操作碼。為這兩個TR指令創(chuàng)建的IL創(chuàng)建過程會比識別出這一模式而創(chuàng)建的代碼的效率低得多。幸好,由于OOCT是軟件,增加一個新代碼是容易的,它有一個特殊單元去識別模式,并代之以產(chǎn)生高效率的IL。在對給定的操作碼產(chǎn)生標(biāo)準(zhǔn)的IL之前,OOCT_opcode_combine例行程序被調(diào)用。這一例行程序重復(fù)已被定義的所有模式,試圖使用一個‘邏輯’操作碼,如果它合適的話。當(dāng)前只定義了兩種模式,但定義其他組合是直接了當(dāng)之事。如果模式之一被匹配,則使用那個邏輯操作碼的建立過程去創(chuàng)建IL指令,而且OOCT_opcode_combine將返回TRUE(真)以指明不需要調(diào)用正常的IL建立過程。ILBuildingProcedures(IL建立過程)(compiler/ooct_il_build.c)對于每個K操作碼,有一個特定的IL建立過程。IL建立過程取兩類變元該指令地址和作為原始指令中字段的變換的列表。IL建立過程還使用一個共享全局變量global_gen_state,它用于在產(chǎn)生IL過程中跟蹤偽寄存器和標(biāo)號。每個IL建立過程向IL_CTXT結(jié)構(gòu)中添加IL指令。所有產(chǎn)生IL的例行程序都創(chuàng)建標(biāo)號IL_NODE,并以原始指令的地址作為標(biāo)號的標(biāo)識符(如果該標(biāo)號不是另一指令的目標(biāo)。它將在優(yōu)化過程中的早期被刪除),一般不試圖完成優(yōu)化,把優(yōu)化留給編譯器104的以后階段,但有少數(shù)特殊情況是要處理的,如對能在編譯時檢測出來的例外進行檢驗。一旦熟悉了IL以及要產(chǎn)生代碼的原始指令,那么大多數(shù)IL建立過程是直接了當(dāng)?shù)?。有幾點小技巧可幫助理解代碼IL的建立被設(shè)計成對任何給定操作碼的編譯能被容易地關(guān)掉以進行調(diào)試。這主要由REALLY_COMPILE宏和COMPILE_SECTION_XX宏來控制。當(dāng)REALLY_COMPILE被關(guān)掉時,所有IL建立例行程序都將只建立一個調(diào)(或跳)回到解釋器110。當(dāng)COMPILE_SECTION_X被關(guān)掉時,對于段號X中的操作碼,所有IL建立例行程序都將只建立一個調(diào)(或跳)回到解釋器110。因為IL是分類的,所以使用具有正確類型的正確大小偽寄存器是至關(guān)重要的。例如,把一個16位值加載到32位寄存器中,首先對一個16位偽寄存器完成16位加載,然后使用一個CVT操作把這個16位值加成一個32位值(LOAD_CVT32宏完成這件事)。每當(dāng)插入一個到解釋器110的回調(diào)或轉(zhuǎn)跳,都必須添加一個SYNC以保證解釋器對K寄存器有正確值。編譯后代碼在其向前運行時并不試圖保持ESI寄存器之值(事實上該寄存器被用于保持其他值)。這樣,所產(chǎn)生的代碼必須在調(diào)用或轉(zhuǎn)跳到解釋器110之前把正確值放入ESI。當(dāng)進行回調(diào)時,代碼還必須使每個偽寄存器含有一個EXTMOD指令,因為偽寄存器可能已被該回調(diào)所修改(MODIFIES_REG宏完成這件事)。處置例外條件(如溢出)的代碼不在線上。代之以在IL指令列表的結(jié)尾處產(chǎn)生代碼。這允許這種共同的情況被編譯成一種失敗,這一般會改善所產(chǎn)生代碼的性能。入點口,中斷檢驗除了為塊檢出器114選取的每個K操作碼產(chǎn)生IL外,還為入口點和中斷檢驗產(chǎn)生IL。為了允許發(fā)生更多的優(yōu)化,沒有把每個分支目的地作為外部入口點包括在內(nèi)(外部入口點的作用是阻礙優(yōu)化)。具體而言,應(yīng)該構(gòu)成外部入口點的唯一指令是那些從段外轉(zhuǎn)跳來的指令。當(dāng)編譯一給定段時,在分支日志中可得到關(guān)于哪些指令滿足這一判據(jù)的部分信息(見前文關(guān)于分支日志的信息)。編譯器104使用這一信息去選擇那些應(yīng)該有外部入口的基本塊。對于每個這種入口,產(chǎn)生一個ENTRY_IL_NODE以及一個GOTOIL_NODE,后者轉(zhuǎn)跳到為入口原始指令所產(chǎn)生的IL。OOCT說明書指出,編譯器104應(yīng)在任何循環(huán)內(nèi)插入中斷檢驗。在產(chǎn)生IL時,通過在段內(nèi)的任何向后分支內(nèi)和在任何計算出的轉(zhuǎn)跳指令之前插入中斷檢驗,從而構(gòu)成一種保守的估計。中斷檢驗插在該基本塊中用于最后一條原始指令的標(biāo)號之后。像其他例外條件一樣,用于中斷的IL代碼也產(chǎn)生在線外,從而使正常情況簡單地成為該條件分支的失敗情況。編譯器中間目標(biāo)描述概述編譯器104的‘中間目標(biāo)’的主要目的是改善IL的質(zhì)量,從而在產(chǎn)生代碼階段中將產(chǎn)生更好的代碼。編譯器104的其余部分被構(gòu)成為一系列掃描過程,它們或者完成對IL的分析,或者完成修改IL的變換。這些掃描過程可被應(yīng)用多次,盡管各掃描過程之間有某些依賴性。從這一點起,編譯器104的其余部分不再有關(guān)于K指令的任何知識,它只處理IL。這一部分的其余內(nèi)容分成第一,討論完成OASSIGN插入的階段。第二,討論編譯器104的分析掃描過程。最后,討論編譯器104的變換掃描過程(它完成主要的優(yōu)化)。圖15具體說明了一個OASSIGN插入的實例。OASSINGINSERTION(compiler/ooct_add_overlap_defs.c)。OASSING操作碼是一個特殊的標(biāo)記指令,它使偽寄存器之間的混淆成為顯然的。在OOCT中產(chǎn)生對OASSIGN的需求是因為某些K操作碼使用16位寄存器而其他操作使用32位寄存器,它們與16位寄存器有混淆。在OOCT中,對所有16位和32位寄存器使用單獨的偽寄存器。這樣,某些偽寄存器隱形地彼此重疊。這造成兩個問題。第一個問題是在優(yōu)化掃描過程中產(chǎn)生不正確的變換。對于每個偽寄存器定義,編譯器104跟蹤使用該定義的那些指令,而對于每個偽寄存器應(yīng)用,編譯器104跟蹤它的定義。這一信息稱作use/def(應(yīng)用/定義)信息。編譯器104在諸如常數(shù)映射掃描過程等掃描過程中使用use/def(應(yīng)用/定義)信息。當(dāng)偽寄存器能彼此混淆時,這需要use/def(應(yīng)用/定義)計算和使用該信息的編譯器104掃描過程變得復(fù)雜得多。由于偽寄存器重疊造成的第二個問題出現(xiàn)在寄存器分配當(dāng)中。當(dāng)寄存器分配器把兩個重疊的偽寄存器同時賦予機器寄存器時,對一個寄存器的修改可能要求另一寄存器變?yōu)闊o效。一般而言,跟蹤這種信息是很困難的。而且造成不必要的復(fù)雜性。不去對付這些困難問題和顯著增加編譯器104的復(fù)雜性,代之以設(shè)計了一種插入特殊標(biāo)記器OASSIGN指令,它會允許編譯器104忽略這個問題。在產(chǎn)生IL之后緊跟著的一個特殊編譯器掃描過程插入OASSIGN指令。在這個編譯器104掃描過程之后,則允許其他分析掃描過程段定偽寄存器不重疊(針對use/def)(應(yīng)用/定義)分析。此外,使用OASSIGN指令能相當(dāng)容易地處置寄存器分配。每當(dāng)寄存器分配器到達一個OASSIGN指令時,它在源的定義處把源瀉出并把目的地填在OASSIGN之后。這一方法使用被混淆的存儲器以保證對重疊定義的任何應(yīng)用都使用正確值。在兩個階段中處理OASSIGN插入。第一,運行UseDef(應(yīng)用-定義)分析的特殊版本。UseDef的這一版本知道偽寄存器的重疊,并創(chuàng)建含有重疊偽寄存器的應(yīng)用列表和定義列表。編譯器104的其余部分不準(zhǔn)備去處理含有重疊偽寄存器的use/def列表,所以UseDef的這一選項不應(yīng)普遍使用。在完成這一分析之后,過程OOCT_Add_Overlap_Defs完成OASSIGN的實際插入。對于有重疊定義(該定義定義一個與應(yīng)用的偽寄存器重疊的偽寄存器)的每個應(yīng)用和對于在標(biāo)號處達到這些定義的重疊,插入一個OASSIGN。圖15說明了要插入OASSIGN的情況的實例。在此例中,偽寄存器GRPAIR1和GR1重疊,所以在代碼第一行中對GRPAIR1的賦值是對GR1的隱含修改。OASSIGN使這種情況變成顯然的。分析掃描過程UseDef(compiler/oc_common/src/oc_usedef.c)最基本的編譯器104分析之一是計算給定定義的應(yīng)用及給定應(yīng)用的潛在定義。每個編譯器104優(yōu)化掃描過程使用這個use/def信息。每條IL指令可以有一個被寫入的偽寄存器變元(dest(目的地))和一個或多個從中讀出用的偽寄存器變元(src(源))。在UseDef分析之后,每個dest伴有一個列表,它存儲指向所有可能應(yīng)用該值的IL指令的指針(稱作du鏈)。類似地,每個src伴有一個列表,它存儲指向所有可能定義該值的IL指令的指針(也稱作ud鏈)。下面描述計算use/def信息的方法。它是一個試圖到達一固定點的疊代方法(即直至進一步疊代不造成改變時為止)。重復(fù)下列步驟,直至對于在任何標(biāo)號處的達到定義沒有變化為止。消除regdefs中每個偽寄存器的定義列表(由偽寄存器索引的NL_LIST陳列。按靜態(tài)程序順序在IL_NODE上疊代。如果該指令使用一個偽寄存器,則把該偽寄存器的定義從regdefs復(fù)制到該操作數(shù)的ud鏈中。如果該指令是一個分支指令,則把regdefs與存在該分支標(biāo)號(LABEL)中的達到定義組合。改變到該達到定義使整個循環(huán)被重復(fù)。如果該指令是一個標(biāo)號(LABEL),則把該regdefs與已在該標(biāo)號處的達到定義組合。如果該指令定義一個偽寄存器,則讓regdefs中的定義列表只包含這一指令。如果該指令是一個無條件分支指令,則把regdefs數(shù)組改變成存在下一個標(biāo)號(LABEL)中的達到定義集合。這樣做的原因是這些指令在它們的靜態(tài)順序中被處理,而達到該無條件分支的那些定義與達到其靜態(tài)后繼者的那些定義是不同的。有效變量分析(compiler/oc_common/src/oc_usedef.c)分析的另一種形式用于有效變量分析。有效變量分析主要用于寄存器分配,但也能用于引入變量變換和死代碼刪除。如果一個偽寄存器在被重定義之前可以沿一執(zhí)行路徑被使用,便認(rèn)為該偽寄存器在程序中的一個特定點是有效的(live)。有效變量分析還標(biāo)記出一給定偽寄存器的最后一次使用(如果在一偽寄存器被重定義之前再沒有使用該偽寄存器的任何可能執(zhí)行路徑,則這次使用是最后一次使用)。下面描述計算有效變量信息的基本方法。它的工作方式是在代碼上重復(fù)向后掃描過程,直至達到一個固定點。重復(fù)下列步驟,直至對于在任何標(biāo)號處的達到定義沒有變化為止。清除live(有效)(偽寄存器的一個位組)按反靜態(tài)程序順序在IL_NODE上疊代。如果該指令使用一個偽寄存器,則設(shè)置live位組中的該偽寄存器位。如果以前該偽寄存器不曾有效,則把它標(biāo)記為最后一次使用。如果該指令是一個分支指令,則把live位組與存在分支LABEL(標(biāo)號)中的有效寄存器組合。改變有效寄存器使整個循環(huán)被重復(fù)。如果該指令是一個標(biāo)號(LABEL),把live位組與已在該標(biāo)號處的有效偽寄存器組合。如果該指令定義一個偽寄存器,則從live位組中清除該偽寄存器。如果該指令是一個無條件分支指令,則清除live位組。這樣做的理由是按其反靜態(tài)順序處理這些指令,而在無條件分支處的有效變量不同于在其后繼者處的那些有效變量。寄存器分配(compiler/oc_common/src/oc_regalloc.c)在編譯器104中的寄存器分配以兩階段完成。第一階段完成代碼分析并根據(jù)目標(biāo)機器的一個高級模型確定一組推薦的寄存器賦值。第二階段使用來自第一階段的分析結(jié)果以及不那么抽象的機器模型,去實際修改使用物理寄存器的代碼。這一部分將討論這第一階段。寄存器分配方法是基于使用圖染色的傳統(tǒng)技術(shù)。該“圖”的節(jié)點是偽寄存器有效范圍,在各有效范圍之間帶有重疊的邊緣。一個N色圖染色把N個顏色之一賦予每個節(jié)點,從而沒有任何兩個相連的節(jié)點有相同的顏色。顯然,如果這個有效范圍圖能有N種顏色(這里N是可用的物理寄存器的個數(shù)),則每個有效范圍被賦予一個寄存器??上В瑘D染色是一個NP硬問題(即它需要指數(shù)時間),所以在實際的試探法中是不使用的。寄存器分配是一個復(fù)雜的多步驟過程。下面詳細描述這些步驟。1.分開獨立的有效范圍并分配REGINFO結(jié)構(gòu)ComputeRegInfo函數(shù)做這件事。它把每個偽寄存器分開成獨立的有效范圍,并為每個有效范圍分配一個REGINFO結(jié)構(gòu)。REGINFO結(jié)構(gòu)用于容納關(guān)于所考慮的有效范圍的信息,該信息用于寄存器分配,REGINFO結(jié)構(gòu)最終容納“目標(biāo)”寄存器一為該有效范圍分配的物理寄存器。由于偽寄存器有效范圍(一個邏輯結(jié)構(gòu))和REGINFO結(jié)構(gòu)之間有1∶1對應(yīng)關(guān)系,所以名詞REGINFO常用于指有效范圍和數(shù)據(jù)結(jié)構(gòu)二者。ComputeRegInfo作有效范圍分開這件事幾乎是分配REGINFO結(jié)構(gòu)的副產(chǎn)品。它的工作從一個尚沒有REGINFO的定義開始,為它創(chuàng)建一個REGINFO,然后遞歸地觀察它的全部應(yīng)用和它們的全部定義,(以及它們的全部應(yīng)用…)并把新的REGINFO與每個定義和可達到的應(yīng)用關(guān)聯(lián)起來。一旦創(chuàng)建了所有REGINFO,這些REGINFO被分開成‘簡單的’和‘復(fù)雜的’。一個‘簡單的’REGINFO正好有一個定義和一個應(yīng)用這應(yīng)用緊跟在定義之后這應(yīng)用不是一個BINOP(目標(biāo)特定要求)的第二操作數(shù)。所有其他REGINFO都是復(fù)雜的。每個REGINFO被給于一個唯一的ID(標(biāo)識)。復(fù)雜的REGINFO在必需選擇能容納這一類型的值(信息來自MachineModel(機器模型))。INUSE[required(需要的)]不能選擇一個寄存器,如果它已被分配給沖突的REGINFO(或與其重疊的任何東西)BASEREGS[required(需要的)]不能選擇一個寄存器,如果該框架已保留它作為某種frame/stack/base指針CLOBBERED(被弄成亂碼的)盡量不要使用這樣一個寄存器,它在REGINFO生命時間內(nèi)已被某人弄成亂碼了DEFCONSTRAINTS(DEF約束)對于定義這個REGINFO的每條IL,盡量使用滿足來自機器模型的DEST約束的寄存器USECONSTRAINTS(USE約束)對于定義這個REGINFO的每條IL,盡量使用滿足來自機器模型的SRC約束的寄存器COMPATABILITY(相容性)盡量使用這樣的寄存器,它與相容性列表中已被賦予寄存器的另一REGINFO相容。一旦所有REGINFO已被賦予寄存器(或失敗了),它在REGINFO上進行另一個掃描過程,通過相容性約束查尋要改變的寄存器(即在這個之后賦予的相容REGINFO,由于某種其他原因它不能進入這同一寄存器)。變換(優(yōu)化)掃描過程變換掃描過程處于優(yōu)化編譯器104的心臟。每個掃描過程試圖重寫部分代碼,從而使代碼的含義保持不變但所產(chǎn)生的最終代碼將運行得更快些。某些變換掃描過程本身并不改善代碼質(zhì)量,而是它們允許其他掃描過程去改善代碼。這樣,這些過程趨于組合起來做得最好,而當(dāng)單獨使用時便不那么有效。由于這一原因,許多掃描過程,如刪除死代碼(DeadCodeElimination)被重復(fù)地運行。刪除死代碼(compiler/oc_common/src/oc_usedef.c)刪除死代碼掃描過程(OC_ElimDeadCode)根據(jù)數(shù)據(jù)流和控制流信息去掉所有死代碼。數(shù)據(jù)流信息用于刪除沒有副效應(yīng)和其結(jié)果不被使用的那些IL_NODE??刂屏餍畔⒂糜谌サ裟切⒂啦粫粓?zhí)行的所有IL_NODE(不可達到的代碼)。此外,完成某些分支的目標(biāo)重定。所用方法掃描如下重復(fù)下列步驟,直至沒有任何改變?yōu)橹埂?.按靜態(tài)程序順序在IL_NODE上疊代。a)如果該指令是不可達到的,則刪掉它。如果一條指令是一個標(biāo)號(LABEL)而沒有任何其他指令以它為目標(biāo),或者如果它是一個GOTO或CGOTO到下一指令的指令,或者如果該指令直接在一無條件分支指令之后而又不是一個標(biāo)號,那么這條指令便是不可達到的。b)如果該指令沒有副作用而且它除其本身外無其他任何應(yīng)用,則去掉它。c)如果一固定分支指令轉(zhuǎn)跳到一個無條件分支,則重定該指令的目標(biāo)(例如GOTO到一GOTO)。d)查出指向下一條指令的條件分支,而且該條件分支后接的是到其他某個地方(L2)的分支指令。在這種情況中,條件被轉(zhuǎn)換,該條件分支被重定目標(biāo)到L2。圖16具體說明刪除死代碼的一個實例,以及地址檢驗刪除(compiler/ooct_clim_achk.c)。地址檢驗刪除掃描過程使用數(shù)據(jù)流分析技術(shù)去刪除不必要的地址對齊檢驗。代碼通過在偶數(shù)和奇數(shù)代數(shù)上完成值推斷來進行工作。換句話說,對代碼進行分析,以確定在任何給定點一個偽寄存器是否保持有一個偶數(shù),奇數(shù)或未知值。這一分析是在全局進行的并且穿過分支進行工作。這表明它將對循環(huán)工作而且通過其他控制流工作,如果完成的是單個不滾動循環(huán),則工作得特別好。下面描述所用的方法。這是一個疊代方法,它試圖達到一個保守的固定點。以三種主要方式對值進行推斷。第一,當(dāng)一偽寄存器被賦予一常數(shù)時,該值能被推斷。第二,當(dāng)一偽寄存器是具有已知變元的操作的結(jié)果時,該值能被推斷。第三,條件分支指令給出關(guān)于偽寄存器值的信息。例如,如果一個偽寄存器被測試看其是否為偶數(shù),則沿著一個分支我們知其為偶數(shù),而沿另一分支它是奇數(shù)。重復(fù)下列步驟,直至在任何標(biāo)號處所推斷值沒有任何改變?yōu)橹埂?.清除infvals(由偽寄存器給出索引的INFVAL數(shù)組)中每個偽寄存器的定義列表。2.按靜態(tài)程序順序在IL_NODE上疊代。a)如果在給定當(dāng)前已知推斷值的情況下該指令能被簡化,則用更簡單的形式代替該指令。對該指令的改變使整個循環(huán)被重復(fù)。b)根據(jù)當(dāng)前指令的執(zhí)行情況更新infvals。i)如果該指令是條件指令,對此條件能推斷一值,則以適當(dāng)?shù)耐茢嘀等ジ履切┐鎯υ谠撃繕?biāo)LABEL(標(biāo)號)處和在CGOTO處的推斷值。ii)如果該指令是無條件的而且定義了一個偽寄存器,則更新infvals中那個偽寄存器的值。這個值是未知的,除非該操作是SET,或者是一種特殊情況,如兩個偶數(shù)相加。c)如果該指令是一個LABEL,則把infvals與已在該標(biāo)號處的推斷值組合在一起。d)如果該指令是一個分支,則把infvals與存在該分支的LABEL(標(biāo)號)處的推斷值組合在一起。對infvals的改變使整個循環(huán)被重復(fù)。e)如果該指令是一個條件分支,從那個條件推斷出的任何值都與infvals組合在一起。f)如果該指令是一個無條件分支,把infvals數(shù)組改變成存儲在下一個LABEL(標(biāo)號)處的推斷值。這樣做是按其靜態(tài)順序處理指令,在無條件分支處的推斷值不同于在其靜態(tài)后繼者處的那些推斷值。圖17具體說明地址檢驗刪除的一個實例。為改善該分析的性能,一個偽寄存器可取簡單的ODD(奇數(shù))、EVEN(偶數(shù))或UNKNOWN(未知)以外的其他值。一個偽寄存器還可標(biāo)記為EQUIVALENT(等效于)另一偽寄存器或EQUIVALENT(等效于)兩個偽寄存器的一個二元操作。這通過允許關(guān)于一個寄存器的信息傳播到其他偽寄存器來改善該分析的能力。例如假定發(fā)現(xiàn)偽寄存器R1和偽寄存器R2是等效的。如果該方法能表明R1是偶數(shù)(例如通過分支測試結(jié)果),那么R2必定也是偶數(shù)。請注意,該方法是一個保守的方法,被推斷的值必須是單調(diào)增加的。換句話說,如果在執(zhí)行該方法過程中的任何時候確定在程序中一點處的一個值是EVEN(偶數(shù)),它必定是該值真正為EVEN(偶數(shù))的情況。該方法決不會指出在一次疊代過程中一個偽寄存器是EVEN(偶數(shù)),而在另一疊代過程中它是UNKNOWN(未知的)。由這一性質(zhì)導(dǎo)出該方法的結(jié)束是直接明了的。吊起(Hoisting)(compiler/oc_common/src/oc_hoist.c)吊起,通常是指循環(huán)不變代碼運動,它是把對一個循環(huán)而言為常數(shù)的計算移到該循環(huán)之外的過程。這通常會顯著提高速度,因為該代碼將只被執(zhí)行一次,而不是對每次循環(huán)疊代都執(zhí)行一次。1.對IL重新編號(即使將標(biāo)識碼按順序排列)2.對于每個向后分支(即潛在的循環(huán)),盡量把一些東西吊出去。a)如果有進入該循環(huán)的另一入口,則沒有任何東西要從該循環(huán)中吊出去b)按靜態(tài)順序在該循環(huán)內(nèi)部在IL_NODE上進行疊代。i)如果一個節(jié)點滿足下列條件,則它能被吊出(a)它沒有使用或定義一個‘實整數(shù)’(b)它沒有使用在該循環(huán)內(nèi)設(shè)置的一個偽寄存器(c)它沒有副效應(yīng)ii)對任何能被吊出的操作碼,重命名它定義的任何偽寄存器iii)把該IL_NODE移到該循環(huán)的上方iv)對所有IL_NODE重新編號v)如果檢測到一個分支,則跳到該分支的目標(biāo)處(因為不能確定該分支是否被執(zhí)行,所以該代碼不能被吊出)。對于OOCT,吊出掃描過程并不總是有效。其主要理由是許多循環(huán)也是入口點,所以它們有進入循環(huán)的多個入口,而且未被吊起掃描過程查看。這個問題能通過完成‘標(biāo)號分開’加以修補,在標(biāo)號分開過程中能創(chuàng)建一個新的標(biāo)號作為該循環(huán)的目標(biāo)。于是被吊起的操作能被抬到原始標(biāo)號和新創(chuàng)建標(biāo)號之間。不久將實現(xiàn)這一點。公共子表達式刪除(CSE)(compiler/oc_common/src/oc_cse.c)公共子表達式刪除(CSE)是一種技術(shù),其目的是刪除冗余計算。編譯器104使用一種全局CSE方法。下面結(jié)合圖18中的說明實例,描述該基本方法。1.當(dāng)進行改變時,對每個有目的地的IL_NODE(實例中的第一行),做如下事情a)按時檢驗該目的地的所有應(yīng)用,看是否一個控制其他(如果到B的所有路徑必須通過A,則稱A控制B)。對于每個這種A、B對(行2和行4),做如下事情ii)檢驗A和B是否‘匹配’(相同操作碼,或相同源),如果不匹配,則到下一對表達式。A和B是一個‘公共子表達式’。iii)按下述方式從A和B開始盡量找出更大的公共子表達式。如果A和B有目的地,而且B的目的地有一唯一應(yīng)用C(行5),檢驗A的目的地是否有任何應(yīng)用D(行3)使得D控制C而且D與C匹配。如果是這樣,把D和C添加到公共子表達式中,并試圖找到具有A=D,B=C的更大的子表達式。iv)既然我們有兩個公共子表達式A(行2,3)和B(行4,5)存在,那我們需要重寫代碼以使對B的應(yīng)用現(xiàn)在由A代替。如果A的目的地在被B使用之前可能會改變,則對新的偽寄存器使用一個復(fù)制。圖18具體說明公共子表達式刪除(“CSE”)的一個實例。復(fù)制傳播(compiler/oc_common/src/oc_copyprop.c)復(fù)制傳播是一種變換,它試圖以賦值的源代替對賦值的目標(biāo)的使用。盡管復(fù)制傳播本身并不改善代碼質(zhì)量,但它經(jīng)常產(chǎn)生代碼使那里的賦值結(jié)果不再被使用,這樣該賦值能被刪除。下面描述復(fù)制傳播的方法。1.對每個ASSIGN(賦值)操作。a)如果ASSIGN的源是一簡單定義而且該定義的使用只是這個ASSIGN,而且該ASSIGN的目的地在定義和ASSIGN之間既未被修改也未被使用,則把該定義修改成對ASSIGN目的地的定義,并刪去這個ASSIGN。b)對ASSIGN目的地的每個應(yīng)用,測試該ASSIGN是否是那個應(yīng)用的唯一定義并測試該ASSIGN的源在ASSIGN和該應(yīng)用之間是否即存在且有效。如果這兩個測試為真,則以源的應(yīng)用替代該目的地的應(yīng)用。圖19具體說明一復(fù)制傳播的舉例。圖20具體說明一常數(shù)合并的舉例。常數(shù)合并(ConstantFolding)(compiler/oc_common/src/oc_cfold.c)常數(shù)合并是一種變換,它在編譯時間對于常數(shù)值操作進行求值。例如,如果IL把兩個常數(shù)值相加在一起,常數(shù)合并將以一個SET指令代替那些IL指令,該SET指令把該相加的目的地賦值為這兩個數(shù)值之和。常數(shù)合并掃描過程所用方法是很直接了當(dāng)?shù)?。每個IL指令被按順序檢驗。對每個算術(shù)的和邏輯的操作(ADD,SUB,BAND,BOR,等),如果它的變元全為常數(shù),則該IL操作以一SET操作代替,該SET操作設(shè)目的地偽寄存器為對常數(shù)變元操作的結(jié)果。模式匹配(PatterMatching)(compiler/oc_common/src/oc_pattern.c)編譯器104還有一個模式匹配優(yōu)化掃描過程,它以更有效的形式替代IL指令的已知模式。目前沒有模式能共同地匹配由OOCT產(chǎn)生的IL指令,所以模式匹配掃描過程沒有運行。產(chǎn)生目標(biāo)碼在已產(chǎn)生IL并已應(yīng)用若干變換來改善代碼質(zhì)量之后,三個主要的編譯器104掃描過程被用于產(chǎn)生代碼,到這一點為止,IL和變換掃描過程都一直是獨立于機器的,但這三個掃描過程則強烈依賴于目標(biāo)的體系結(jié)構(gòu)。指令合并(INSTRUCTION/FOLDING)/(compiler/oc_ommon/src/ix86_ifold.c)。OOCT是類似于RISC的結(jié)構(gòu),不經(jīng)修改則不能有效地映射到目標(biāo)結(jié)構(gòu)。具體而言,對每個IL指令產(chǎn)生一個目標(biāo)指令將是不那么有效的。因為目標(biāo)結(jié)構(gòu)是一個CISC結(jié)構(gòu),多個IL指令常能被合并成單個目標(biāo)指令。所設(shè)計的指令合并掃描過程通過標(biāo)記出能被組合成單個目標(biāo)指令的IL指令組來解決這一問題。指令合并掃描過程通過查尋大量預(yù)先定義的不同指令組合之一來進行工作。下列組合被使用·常數(shù)被合并成ADD、SUB等各種操作。·SETCC指令被合并成它們在設(shè)定條件代碼時所基于的指令。·具有相同變元的DIV、REM對被合并到一起?!DD、SUB和ASL操作能被合并成單個‘lea’操作,或者合并成LOAD或STORE的地址計算?!?6位BSWAP、STORE組合被合并成兩個單獨的8位存儲?!OAD操作被合并成各種操作,此時它們的結(jié)果被用作為第二變元。指令合并掃描過程只是簡單地決定指令是否應(yīng)被合并,它并不作實際的合并,實際的合并留給產(chǎn)生機器代碼掃描過程。指令合并掃描過程以兩種方式標(biāo)記出要被合并的指令。第一,節(jié)點的每個操作數(shù)可被標(biāo)記為一‘合并’位。第二,以IL_COMBINE(IL_組合)標(biāo)志和mmFold字段標(biāo)記出其全部應(yīng)用均被合并成另一指令的那些指令,這里mmFold字段給出關(guān)于該指令合并方式的信息。寄存器分配器和產(chǎn)生機器代碼都使用這些字段以便正確地完成任務(wù)。目標(biāo)寄存器分配(TargetREGISTERALLOCATION)(compiler/oc_common/src/ix86_regalloc.c)一旦寄存器分配器對所有REGINFO已檢出了它能檢出的寄存器,便有必要遍歷該代碼并對其進行修改,以使用那些物理的寄存器代替?zhèn)渭拇嫫?。此外,有必要把某些額外的偽寄存器暫時放入真實寄存器中,從而使匯編器能為那些指令產(chǎn)生代碼。這通常需要插入分流(spill)和填充(fill)代碼以存儲和恢復(fù)由RegAlloc曾放在那些寄存器中的值。為此,OC_RegUseAlloc使用一個約束分配器(GetReg),并插入分流和填充以重新使用寄存器。OC_RegUseAlloc在代碼上做單次掃描過程,修改和跟蹤‘stat’數(shù)組中的物理寄存器狀態(tài)。這stat數(shù)組記錄任何給定時刻每個寄存器中是(或應(yīng)該是)什么,以及在該寄存器或分流處(或這二者)中的值是否正確。OC_RegUseAlloc的工作作為一系列階段,每個階段對當(dāng)前被處理的指令進行特定的修改。如果有多個IL指令已在指令合并掃描過程中被合并到一起,它們便作為單個指令對待。這些階段如下述1.如果該指令直接使用任何物理寄存器,要確保在這一應(yīng)用之后才發(fā)生對那些寄存器的任何填充。修改該指令,以使用由RegAlloc分析為偽寄存器分配的寄存器。鎖住所有寄存器以使它們將不被重新使用。2.修改指令,以使用由先前對GetReg的調(diào)用暫時分配的寄存器。鎖住所有這些寄存器。3.清掉stat數(shù)組中的狀態(tài)信息,以反映被該指令亂碼的任何寄存器,必要時插入分流。把目的地寄存器改變成由RegAlloc分配的寄存器,如果存在的話(請注意,沒有必要鎖住這個寄存器,因為需要時它能用于容納一個src(源))。4.修改代碼,以把寄存器中的源放到產(chǎn)生目標(biāo)代碼所需要的地方。這涉及調(diào)用GetReg以得到需要在寄存器中的那些源操作數(shù)。5.對所有已被鎖住的寄存器解鎖。6.修補目的地,以在目標(biāo)代碼需要的地方使用真實寄存器。這涉及調(diào)用GetReg。7.使stat數(shù)組確定下來,以反映這一操作的結(jié)果,修補所有使用過的寄存器,把它們的‘以前’位置設(shè)置到下一指令中(從而使任何分流/填充都放在這一完成后的指令之后)。理解stat數(shù)組是重要的。它是由物理寄存器編索引的數(shù)據(jù)結(jié)構(gòu)數(shù)組(在MM_NumRegs以下的所有寄存器都是物理寄存器),它指出那個給定物理寄存器的狀態(tài)。該結(jié)構(gòu)包含以下字段1.riREGINFO結(jié)構(gòu),標(biāo)識當(dāng)前與這真實寄存器關(guān)聯(lián)的偽寄存器(可以是0,以指示無關(guān)聯(lián))。這或者可以是由RegAlloc分配給該寄存器的一個偽寄存器,或者是由GetReg暫時指定的一個。2.alt_ri一個REGINGO結(jié)構(gòu),標(biāo)識一個也在這一寄存器中的額外偽寄存器。這用于GetReg把一偽寄存器賦予一個物理寄存器而RegAlloc同時把另一個放在這里(在ri中)的時候。3.flags標(biāo)識寄存器狀態(tài)的標(biāo)志。例如,RegValid用于指出該寄存器中之值是有效的。如果RegValid未被設(shè)置,該寄存器在能被使用之前必須被填充。見ix86-regalloc以得到對可能的標(biāo)志的完全描述。4.before該指令應(yīng)把對這一寄存器的分流或填充放在什么地方。產(chǎn)生機器碼在兩個掃描過程中產(chǎn)生該目標(biāo)的機器碼。第一掃描過程用于確定指令的長度,從而能計算出分支偏移。第二掃描過程完成實際的產(chǎn)生代碼過程。這兩個掃描過程是完全相同的,只是第一掃描過程把代碼產(chǎn)生到一個暫存緩沖器和沒有正確的分支偏移,所以幾乎所有代碼都是共享的。兩個掃描過程由依序通過IL指令的一個單一掃描過程構(gòu)成。對于每個指令,由操作碼和類型構(gòu)成索引的一張表用于提取一函數(shù)以產(chǎn)生該代碼。這些產(chǎn)生代碼的函數(shù)使用EMIT宏,這些宏是產(chǎn)生目標(biāo)指令的一般化方法,無需知道目標(biāo)的內(nèi)部細節(jié)(見ix86_Asm_Emit.[h,c])。這些宏簡化了那些使用任何目標(biāo)編址方式的指令的匯編過程。段管理由OOCT編譯的代碼存儲在一個SEGMENT(段)數(shù)據(jù)結(jié)構(gòu)中。有許多與段管理有關(guān)的重要事項。第一,段有特殊的存儲器分配器去處置段存儲。第二,討論如何創(chuàng)建段和將其安裝到系統(tǒng)中。第三,討論如何刪除段(如果這一選項被啟動的話)。最后,討論段鎖住,它用于段刪除選項被啟動之時。段分配器(compiler/SegAlloc.[h,c])OOCT中段的存儲管理由一個特殊的分配器處置。在OOCT初始化時,段分配器(SegAlloc)被一個大片存儲器初始化。然后,SegAlloc單元提供請求可變大小的未用的一片存儲器的能力(類似于malloc),釋放先前分配的一片存儲器的能力(類似于free),以及請求對當(dāng)前存儲器使用情況進行統(tǒng)計的能力。SegAlloc比ZONE(區(qū))分配器更復(fù)雜,因為它必須處置可變大小的分配。SegAlloc使用一種相當(dāng)標(biāo)準(zhǔn)的分配方法。該分配器維持一個分類排隊的自由片列表,并為分配的塊使用一個32位頭段以指明塊的大小。為分配一片存儲器,查尋這自由列表以找到滿足所請求大小的一片。如果該片的剩余部分大于一個最小大小,它便被分開,并把這剩余部分添加到freelist(自由列表)中。為釋放一片,它被加到freelist(自由列表)中。由于釋放存儲器的速度不是一個至關(guān)重要的因素,所以查尋freelist(自由列表)以尋找相鄰的自由塊,這些塊被組合到單一自由塊中。段創(chuàng)建和安裝(compiler/ooct_trace.c,compiler/SegMgr.[h,c])在編譯的主要階段完成之后,其最終結(jié)果是含有可重定位目標(biāo)碼的一塊存儲器。下一步是為那代碼創(chuàng)建一個段,并把它安裝到為那些段分配的空間中。OOCT_Install完成這一功能。初始時,用于此段的空間被分配在ZONE存儲器區(qū)域。該段以塊檢出器114選出的基本塊列表初始化(從而在其后能查尋這些段以發(fā)現(xiàn)它們是否包含給定的原始指令),并以所產(chǎn)生的代碼進行初始化。對SEGMGR_Install的調(diào)用把該段變成連續(xù)的存儲器塊,并把它復(fù)制到用SegAlloc單元為這些段分配的空間中。在創(chuàng)建段并將其移到段分配空間之后,指出哪些原始指令有為其編譯的代碼的翻譯表需要被更新。對于每個作為外部入口條目的原始指令,利用為那個入口條目產(chǎn)生的代碼中的正確地址來更新翻譯表。此外,該翻譯表用TRANS_ENTRY_FLAG做標(biāo)記,以指出該K指令有一個有效入口條目。段刪除(compiler/ooct_trace.c,compiler/SegDel.[h,c])當(dāng)編譯器104把一入口條目寫入該翻譯表時,它可能重疊寫在已經(jīng)在那里的一個老條目上。沒有任何解釋器110將能讀那老的條目和跳到這老的段。當(dāng)一段在該翻譯表中沒有入口條目時,則沒有解釋器110使用該段,于是它可被刪除,它的存儲器可用于其他段。這一段描述編譯器怎樣檢測出一段可被刪除和然后如何刪除它。通信部分也很詳細地描述了段鎖住和段刪除。當(dāng)編譯器重疊寫翻譯表中的一個入口點時,它把老的入口點放到一個刪除列表上。在安裝一個新塊之后,編譯器104調(diào)用SEGDEL_TryDeletions。這個過程檢驗刪除列表上的每個條目。如果沒有解釋器在使用一個入口點,則該入口點被刪除,從而其后能被重用。每段在其中有一個入口點計數(shù)器。當(dāng)一個入口點被刪除時,編譯器104對包含該入口點的段減少入口點計數(shù)器之值。當(dāng)一段的入口點計數(shù)器達到0時,則沒有任何解釋器110在使用該段而且沒有任何新的解釋器110能跳入它。編譯器104刪除該段,并釋放它的存儲器以供其他段使用。段鎖住到一段的每個入口點有一個計數(shù)器,它的作用是該入口點上的一把鎖。該計數(shù)器記錄正在使用該入口點的解釋器110個數(shù)。當(dāng)該計數(shù)器之值大于零時,該入口點及它的段被鎖住,于是編譯器104將不刪除它們。入口點鎖的最重要特性是鎖住和解鎖該段的指令不是該段本身的一部分。這使得一個解釋器110不可能執(zhí)行該段中的任何指令,除非它持有該鎖。關(guān)于編譯器104和解釋器110的報告很詳細地解釋了這一段鎖機制。其他事項在編譯器104中還有許多其他事項,它們不能很好地放到其他部分中,但理解它們是重要的。堆棧變形(STACKWARPING)(common/ooct_warp.[c,h])初始時編譯器104被分配一個小的堆棧,它不是動態(tài)擴展的。不幸的是,由于編譯器104使用大量遞歸過程,它需要的堆棧大小往往大于所提供的那個。當(dāng)在GranPower上運行程序時,觀察到這樣的情況,即由于堆棧溢出而發(fā)生了編譯器104不能從中恢復(fù)的頁面錯。不去試圖重寫編譯器104的一些部分或確定如何正確處置由于堆棧溢出造成的頁面錯,而是代之以使用比從OOCT_buffer(OOC緩沖器)分配的那個堆棧大得多的堆棧。這個堆棧的選取是要使堆棧大小永不會成為一個限制因素(其他因素,如ZONE的大小,是更大的限制)。為了使用這個堆棧,設(shè)計了一個清潔界面OOCT_Warp_Stack,它允許調(diào)用一個函數(shù)去使用OOCT的大堆??臻g。在從OOCT_Warp_Stack返回時,堆棧指針將不被改變。這樣,當(dāng)經(jīng)由編譯種子的主入口點ooct_Compile_Seed進入編譯器104時,它被使用OOCT_Warp_Stack調(diào)用。認(rèn)定(ASSERTIONS)(common/assert.[c,h])在編譯器104中的代碼有大量認(rèn)定語句。認(rèn)定被用于整個編譯器104以檢驗一致性約束和其他出錯條件。認(rèn)定起兩個主要作用。在調(diào)制環(huán)境中,一個認(rèn)定失敗使程序停止,同時顯示或存儲對追蹤程序有用的信息。在產(chǎn)出環(huán)境中,認(rèn)定用于抓住出錯條件并在這些條件發(fā)生時安全地退出編譯。例如,如果編譯器104運行超出存儲器時,一個認(rèn)定將使編譯器放棄編譯那個種子。服務(wù)例行程序(common/service.h)服務(wù)單元提供的服務(wù)通常在標(biāo)準(zhǔn)C庫中提供,如printf和memset,它們不是由KOI監(jiān)視器提供。這個單元想要抽象掉在Windows(視窗)和固件構(gòu)成中不同地處置這些系統(tǒng)調(diào)用的需要。這些服務(wù)例行程序有兩個底層實現(xiàn),一個用于Wintest目標(biāo),另一個用于固件構(gòu)成。VIII.視窗測試環(huán)境在快速開發(fā)和測試OOCT系統(tǒng)中視窗測試環(huán)境起了至關(guān)重要的作用。通過在視窗下開發(fā),在MSVC下提供了標(biāo)準(zhǔn)調(diào)試工具。其他有用的工具,如profilers,也能得到。為了測試目的,已在視窗下開發(fā)了專用測試方法,它們已提高了測試的速度和測試覆蓋范圍。首先描述仿真Granpower環(huán)境。然后討論實現(xiàn)大多數(shù)高級測試技術(shù)的比較單元。最后描述編譯器104的代碼轉(zhuǎn)儲(dump)仿真GRANPOWER環(huán)境為了完成對OOCT的初始測試以及更高級的測試和性能分析,需要一個會在視窗(Windows)下運行的解釋器。解釋器110本身不需要修改,但需要寫GranPower系統(tǒng)中提供的初始化調(diào)用和AOI系統(tǒng)調(diào)用。此外,對于在視窗下運行的OOCT,需要一種設(shè)計去運行多重‘任務(wù)’,因為編譯器104是作為與解釋器110分離的任務(wù)運行的。初始化創(chuàng)建視窗下仿真環(huán)境的第一部分是創(chuàng)建正確初始化KOI數(shù)據(jù)結(jié)構(gòu)和為OOCT任務(wù)仿真KOI初始化API的代碼。解釋器110期望大量數(shù)據(jù)結(jié)構(gòu)被適當(dāng)?shù)爻跏蓟员隳軋?zhí)行任何代碼。此外,某些數(shù)據(jù)結(jié)構(gòu)元素控制是否使用OOCT。通過把我們的初始化代碼放在固件初始化過程基礎(chǔ)上,仿真正確的初始化以運行解釋器110和控制它的一些基本行為。類似地,以KOI初始化API為基礎(chǔ),用于OOCT任務(wù)去在固件所用代碼上運行。這允許對解釋器110之間界面(如對OOCT_Init的調(diào)用)的初始寫和測試能在標(biāo)準(zhǔn)視窗調(diào)試環(huán)境中工作。它還使改變和測試界面成為直接了當(dāng)之事。AOI系統(tǒng)調(diào)用(wintest/MiscStubs.c,wintest/MsgStubs.c)。解釋器110期望在一個有全部AOI系統(tǒng)調(diào)用可供使用的環(huán)境中運行。為了甚至能編譯和鏈接一個可執(zhí)行程序,需要創(chuàng)建用于AOI系統(tǒng)調(diào)用的存根(stub)。許多系統(tǒng)調(diào)用在視窗下測試系統(tǒng)時并無意義,所以那些調(diào)用只是作為空函數(shù)留下(只是用于鏈接目的)。所提供的AOI系統(tǒng)調(diào)用的實現(xiàn)用于計時(ScGtmSet,ScGtmRef)和用于messsgAlc,ScMsgSnd,ScMsgRev)。OOCT強烈依賴于消息傳遞系統(tǒng)調(diào)用,以用于Exec和編譯器104之間的過程間通信。在視窗下,那些AOI系統(tǒng)調(diào)用的啞形式用于允許同一任務(wù)內(nèi)的各線程進行通信(見前文)。消息系統(tǒng)調(diào)用的視窗版本實現(xiàn)使用鎖住和消息隊列來對系統(tǒng)調(diào)用的完全說明。Compiler/EXEC用的單獨線程為了簡化視窗下的實現(xiàn)和調(diào)試,對編譯器104和解釋器110使用單獨的線程以代替單獨的過程。使用線程簡化了兩個‘任務(wù)’之間的消息傳遞實現(xiàn)。此外,調(diào)試更容易了,因為對兩個任務(wù)(解釋器110和編譯器104)能使用單個調(diào)試器,還因為調(diào)試器被設(shè)計成在多線程上工作(我們不知道有任何調(diào)試器具有調(diào)試多過程的工具)。比較單元OOCT使用一種獨特的測試方法,它已被證明是特別有價值的。因為OOCT編譯后的代碼所產(chǎn)生的結(jié)果應(yīng)與解釋器110產(chǎn)生的結(jié)果嚴(yán)格相同,所以創(chuàng)建了一種途徑去直接比較那些結(jié)果。在視窗測試環(huán)境下,在OOCT和解釋器110兩種情況下運行程序的能力和自動比較中間結(jié)果的能力已被構(gòu)建在其中。這些比較可以任意好地細化,可以細到每條指令之后都進行檢驗。與比較程序行為的能力一起,還寫了一個自動測試發(fā)生器。該測試發(fā)生器創(chuàng)建‘隨機’代碼,然后運行該代碼并進行比較。這個自動測試產(chǎn)生和比較提供了特別大的一套程序來證實OOCT在正確地工作。此外,它已提供了特別有價值的方式去正確地指出發(fā)生的錯誤,因為自動比較能指出編譯后代碼和解釋器110首次發(fā)生差異的地方。這一部分將分兩階段描述比較單元。第一,描述用于比較編譯后代碼結(jié)果和解釋器110結(jié)果的底層結(jié)構(gòu)。第二,描述測試中所用隨機代碼的產(chǎn)生。比較底層結(jié)構(gòu)比較底層結(jié)構(gòu)是基于運行同一K程序的兩個版本這一思想,這里在各指定時刻仿真K機器(寄存器和存儲器)的機器狀態(tài)是被檢驗點。然后對那點檢驗點的結(jié)果進行比較,以確定編譯后版本和被解釋的版本給出相同的結(jié)果。圖21具體說明上述過程的一個實例,它有一個根據(jù)本發(fā)明一實施例構(gòu)成的比較底層結(jié)構(gòu)。在實踐中,這種比較測試是作為兩個視窗過程運行的。父過程運行帶有分支記錄和編譯的整個OOCT系統(tǒng),而子過程只運行KOI的一個被解釋版本。這兩個過程都把它們的檢驗點日志寫入存儲器(子過程寫到共享存儲器)以記錄它們對仿真K機器狀態(tài)的影響。父過程比較日志中的數(shù)據(jù)并報告任何不一致情況。代碼生成由三個單元完成產(chǎn)生比較測試用隨機代碼。第一,K匯編器提供了一種機制用于使用C函數(shù)調(diào)用來產(chǎn)生K機器碼。第二,提供了創(chuàng)建各類K操作基本塊的單元。最后,隨機控制流單元允許產(chǎn)生具有多種不同類型控制流的代碼。K匯編器(wintest/OOCT_Assemble.[h,c])K匯編器提供了一種直接了當(dāng)?shù)臋C制從C程序內(nèi)部產(chǎn)生K代碼。每個K操作碼有一個函數(shù)用于匯編專用于那個操作碼的指令。單個指令取指向存儲代碼存儲器的一個指針、一個(可能是空的)標(biāo)號名、以及該指令中所用每個字段的變元作為該指令的變元。該函數(shù)只是簡單地把這些字段組合到它們的正確位置并把代碼寫入緩沖器。因為可能在定義一標(biāo)號之前發(fā)生到該標(biāo)號的分支,所以使用在代碼上的第二次掃描過程,以還原分支目的地。隨機K操作碼創(chuàng)建單元(wintest/GenArth.c,wintest/GenCassist.c,wintest/GenMisc.c)為了測試各類指令,創(chuàng)建了單個的單元,它們產(chǎn)生含有那些指令類型的基本塊(直線代碼)具體而言,創(chuàng)建了若干單元以產(chǎn)生算術(shù)和移位操作、C輔助指令以及由OOCT實現(xiàn)的所有其他指令。與這些單元的主要接口是通過一個FillBasicBlock例行程序。這個例行程序取存儲器緩沖器和指令個數(shù)作為變元,并把給定的指令個數(shù)(隨機選取的)寫入該緩沖器。FillBasicBlock例行程序從產(chǎn)生指令函數(shù)數(shù)組中隨機選取以添加指令。對每個被產(chǎn)生的K操作碼,這些單元含有一個指令產(chǎn)生函數(shù)。這個產(chǎn)生指令函數(shù)選擇適當(dāng)?shù)碾S機值作為提供給匯編器的變元并匯編這些指令。這些指令不是完全隨機地產(chǎn)生的。相反,它們的產(chǎn)生受到某些限制。例如,當(dāng)隨機選取一個寄存器作為目的地時,便決不用基寄存器。該代碼還被限制于使用若干預(yù)先規(guī)定的存儲器位置,在我們的測試中,這些限制沒有被證明是很有意義的。如果在將來證明它們是有意義的,則有可能通過使用更復(fù)雜的過程來減少一些限制。使用隨機測試是重要的,因為它測試許多指令之間的相互作用,這對于一個編譯器104,例如OOCT,是特別重要的。在OOCT中,由編譯一條指令產(chǎn)生的代碼會不同,基本上取決于周圍的指令。圖22具體說明同一指令有不同的周圍指令時產(chǎn)生代碼的舉例。此外,隨機測試所測試的許多情況,程序員是不會去做的。隨機K操作碼創(chuàng)建單元本身對某些類測試是有效的。例如,當(dāng)實現(xiàn)一個新操作碼時,創(chuàng)建一個簡單的循環(huán)去執(zhí)行使用該操作碼的指令基本塊,這已經(jīng)證明是很有效的方法。盡管單個單元能是有效的,但為了充分測試編譯器104的某些方面,更復(fù)雜的控制流程是需要的。隨機控制流程創(chuàng)建單元(wintest/Gdom控制流程創(chuàng)建單元(GenControl))用于創(chuàng)建測試,這些測試使用比直線代碼更復(fù)雜的控制流程類型。GenControl以一單個基本塊開始,并完成一定數(shù)量的變換(隨機選擇)。當(dāng)前能完成的變換如下所述一個基本塊能被分成兩個基本塊。一個基本塊能被一個菱形代替。這代表一個條件分支,這里兩條路徑又聯(lián)結(jié)回到一起。一個基本塊能由一循環(huán)代替。一個基本塊能由三個基本塊代替,這里對第二個基本塊進行函數(shù)調(diào)用并返回到第三個基本塊。在對基本塊完成了指定數(shù)量的變換后,便存在一個隨機產(chǎn)生的控制流程圖,它需要以指令填入。這由兩部分組成。為產(chǎn)生基本塊本身的代碼,使用前文討論的隨機K操作碼創(chuàng)建單元。第二部分是填入指令以完成分支和循環(huán)。循環(huán)使用一個預(yù)先定義的樣板,它重復(fù)固定次數(shù)。對于條件分支,使用一條隨機測試指令。編譯器代碼轉(zhuǎn)儲為了調(diào)試目的和優(yōu)化目的,在視窗下在OOCT中使用了若干個代碼轉(zhuǎn)儲機制。有兩個主要的轉(zhuǎn)儲機制。第一,在編譯過程中,可以轉(zhuǎn)儲一個代碼列表,它包含被編譯的K操作碼、IL、以及目標(biāo)代碼(如果它已經(jīng)產(chǎn)生的話)。第二類轉(zhuǎn)儲是把目標(biāo)代碼轉(zhuǎn)儲為匯編形式,它可以針對測試目的被重新編譯和鏈接。在某些階段之后,通過轉(zhuǎn)儲IL代碼副本,能檢驗一給定編譯器104優(yōu)化掃描過程效果的正確性和有效性。此外,通過檢驗所產(chǎn)生的最終代碼,人們能人工檢驗編譯器104把每個K操作碼翻譯成IL的良好程度和為每條IL指令和K操作碼所產(chǎn)生的目標(biāo)代碼的質(zhì)量。這些代碼轉(zhuǎn)儲由使用COMBDUMP宏來控制,它被插入編譯器104各掃描過程之間,放在OOCT_Optimize_IL_And_Gen_Code(見compiler/ooct_trace.c)中。這個宏調(diào)用OOCT_Combdump過程(見ooct_combdump.c),它在K操作碼和IL指令上重復(fù)。當(dāng)前用于視窗的概要分析工具不能正確處置動態(tài)產(chǎn)生的代碼。這樣,第二類轉(zhuǎn)儲被使用,從而使來自一個運行的動態(tài)代碼能被作為靜態(tài)代碼用于另一個運行并正確地記錄其概況。這以兩個步驟實現(xiàn)。在第一步中,該程序以O(shè)C_DUMP標(biāo)記(見compiler/ooct_dump.h)進行編譯,這使被編譯的每個K操作碼蹤跡被記錄下來,并使代碼以可重編譯格式轉(zhuǎn)儲到一個文件中。第二,該程序以O(shè)C_USEDUMP標(biāo)志(見compiler/ooct_dump.h)進行編譯和運行,它為先前編譯的代碼關(guān)掉動態(tài)編譯,代之以使用靜態(tài)版本。然后可運行該程序的這一版本,并以一個概要分析器記錄關(guān)于該代碼質(zhì)量的統(tǒng)計情況。本發(fā)明第二實施例動態(tài)優(yōu)化目標(biāo)碼翻譯第二實施例概要結(jié)構(gòu)仿真是指由一個不同的計算機結(jié)構(gòu)去模仿一個計算機結(jié)構(gòu),從而能不加修改地去運行用于原始結(jié)構(gòu)的機器代碼。目標(biāo)碼翻譯是把用于一個計算機結(jié)構(gòu)的機器翻譯成用于一個不同計算機結(jié)構(gòu)的機器碼的過程。所描述的動態(tài)優(yōu)化目標(biāo)翻譯系統(tǒng)使用編譯器優(yōu)化技術(shù)達到的性能高于為結(jié)構(gòu)仿真所用的基于模板的目標(biāo)碼翻譯。第二實施例附圖描述圖23說明根據(jù)本發(fā)明第二實施例構(gòu)成的用于動態(tài)優(yōu)化目標(biāo)碼翻譯的系統(tǒng)配置。圖23是與程序的解釋后執(zhí)行并發(fā)的動態(tài)翻譯的示意圖。每個解釋器能向該編譯器發(fā)出翻譯請求。然后編譯器造成能為解釋器任務(wù)使用的翻譯后代碼。在一個具有多個執(zhí)行單元的機器上,所有過程可以并發(fā)地執(zhí)行。第二實施例詳述動態(tài)優(yōu)化目標(biāo)碼翻譯系統(tǒng)完成一組指令到另一組指令的動態(tài)編譯,以提供高于基于模板的翻譯或解釋后仿真的性能改善。動態(tài)優(yōu)化目標(biāo)碼翻譯系統(tǒng)把任意多個解釋器(它們完成對運行代碼的概況記錄)與一單獨的優(yōu)化編譯器的組合。優(yōu)化編譯器使用來自運行代碼的概況記錄信息,以確定代碼中大量地執(zhí)行的部分。然后這些部分被編譯并提供給解釋器使用。系統(tǒng)的總體結(jié)構(gòu)示于圖23。只有知道指令流程圖才可能完成有意義的編譯器型優(yōu)化。在傳統(tǒng)的編譯器中,流程圖是給定的和很好定義的,因為在優(yōu)化開始之前整個例行程序被完全地分析過了。對于一個結(jié)構(gòu)仿真系統(tǒng),在要被編譯的代碼實際運行之前該代碼是不能得到的。此外,如不實際運行一個程序,一般不能使指令和數(shù)據(jù)區(qū)分開。所以,為確定流程圖,該程序必須運行。一個解釋器被用于第一次運行該程序。在解釋器執(zhí)行該程序時,每次它完成一個分支操作時便通知動態(tài)編譯器。這一信息記錄標(biāo)識某些指令和某些結(jié)合點。隨著該程序的運行,關(guān)于流程圖的信息變得更加完全,盡管永遠不會整個地完全。該系統(tǒng)被設(shè)計成以關(guān)于流程圖的部分信息進行工作優(yōu)化是基于可能不完全的流程圖,而且該系統(tǒng)被設(shè)計成允許在可得到更多信息時替換已被優(yōu)化的代碼。動態(tài)編譯根據(jù)解釋器收集的概況記錄信息選擇要優(yōu)化的內(nèi)容部分。當(dāng)某一分支的執(zhí)行次數(shù)超過一閾值時,那個分支的目的地成為供編譯的種子。種子是分析一部分要被編譯的源指令(作為一個單元)的起始點。這一單元被稱作段。一個段含有對來自種子的源指令進行優(yōu)化造成的指令。它作為一個單元被安裝和去掉安裝。當(dāng)解釋器調(diào)用該編譯器的通知它有一個分支時,如果該目的地的代碼存在,解釋器可以選擇把控制轉(zhuǎn)移到該段中。類似地,該段中可以包含把控制轉(zhuǎn)移回解釋器的代碼。一個段可以是不完全的,只代表來自源程序的可能流程路徑的一個子集。但這種不完全的代表不會干擾仿真的正確操作。如果發(fā)生了一個穿過原始代碼的新的未預(yù)料的流程路徑,那么控制流程將跳回解釋器。然后,這同一段能被替換,以考慮這新的控制流程。第二實施例的特別目的本發(fā)明是應(yīng)用優(yōu)化的目標(biāo)碼翻譯來改進結(jié)構(gòu)仿真系統(tǒng)中的性能。第二實施例摘要所描述的動態(tài)優(yōu)化目標(biāo)碼翻譯系統(tǒng)使用編譯器優(yōu)化技術(shù)實現(xiàn)比基于模板的結(jié)構(gòu)仿真目標(biāo)碼翻譯更高的性能。本發(fā)明是應(yīng)用優(yōu)化的目標(biāo)碼翻譯來改進結(jié)構(gòu)仿真系統(tǒng)中的性能。本發(fā)明第三實施例并發(fā)動態(tài)翻譯第三實施例概要動態(tài)翻譯是在程序運行過程中把一種機器語言的計算機程序翻譯成另一種機器語言的活動。所描述的并發(fā)動態(tài)翻譯系統(tǒng)完成與被解釋程序的執(zhí)行并發(fā)的翻譯。第三實施例附圖描述圖24說明根據(jù)本發(fā)明第三實施例的并發(fā)動態(tài)翻譯所用的系統(tǒng)配置。圖24是與程序解釋執(zhí)行并發(fā)的動態(tài)翻譯的示意圖。每個解釋器任務(wù)能向編譯器任務(wù)發(fā)送翻譯請求。然后編譯器任務(wù)使解釋器任務(wù)能得到翻譯后的代碼。在一個具有多個執(zhí)行單元的機器上,所有過程可以并發(fā)地執(zhí)行。圖25說明把解釋器和編譯器例如在執(zhí)行過程中組合成一個任務(wù)和把它們分開成例如不同的任務(wù)二者之間的區(qū)別;這是根據(jù)本發(fā)明的第四實施例。圖25是具有解釋器和編譯器結(jié)合任務(wù)與分離任務(wù)的延遲示意圖。第三實施例詳述并發(fā)動態(tài)翻譯的目的是通過在解釋器仍在運行過程中把一執(zhí)行中的程序編譯成更有效的形式,從而提供比解釋器強的性能。為了完成與解釋器的執(zhí)行并發(fā)的動態(tài)翻譯,編譯器作為一個單獨任務(wù)運行在一個具有多個執(zhí)行單元的系統(tǒng)上。該編譯器是一個服務(wù)器,它接收翻譯某些指令的請求并以一片翻譯后代碼作為回應(yīng)。把編譯器服務(wù)器安排成一個單獨的任務(wù)有許多好處。第一,不只一個解釋器任務(wù)能向同一個服務(wù)器提出請求。第二,解釋器任務(wù)不必等待得到編譯請求結(jié)果后再繼續(xù)進行。第三,解釋器和翻譯器與其他任務(wù)中的故障隔離。第四,解釋器和編譯器能被獨立地安排時間計劃,從而在多個可用處理器上的工作能更均勻地平衡。下面將更詳細地描述這些好處的每一個。一些現(xiàn)有的動態(tài)翻譯系統(tǒng)不具有單獨的編譯器任務(wù)。來自SunMicrosystem的Java虛擬機器是一個實例[2]。在此虛擬機器中的解釋器能通過調(diào)用一個過程來發(fā)出動態(tài)翻譯請求。在解釋器繼續(xù)執(zhí)行程序之前,它得等待翻譯請求完成。另一個實例是FujitsuOCT動態(tài)翻譯系統(tǒng),它一次翻譯一個指令頁面[1]。在OCT系統(tǒng)中,解釋器在繼續(xù)執(zhí)行之前必須等待翻譯請求完成。也還有可用的翻譯服務(wù)器用于把Java源代碼靜態(tài)翻譯成Java字節(jié)代碼[3]。這些服務(wù)器提供了用于靜態(tài)翻譯但不是用于動態(tài)翻譯的單獨編譯器任務(wù)的優(yōu)點,因為在Java程序運行時它們不操作。單獨編譯器任務(wù)安排的第一個好處是多個解釋器任務(wù)能向同一服務(wù)器提出的翻譯請求。它們不必在它們的可執(zhí)行圖像中包括編譯器代碼,這使它們的可執(zhí)行圖像小得多。它們不必高速緩存解釋器指令和編譯器指令之間或解釋器數(shù)據(jù)和編譯器數(shù)據(jù)之間的沖突。因為在幾乎所有現(xiàn)代處理器上有效地使用高速緩存是重要的,這是一個顯著的好處。單獨編譯器任務(wù)的第二個好處是解釋器看不見編譯器的延遲。圖25說明延遲的差異。應(yīng)用組合的解釋器和編譯器任務(wù)時,在編譯器已結(jié)束翻譯指令之前,解釋器不執(zhí)行任務(wù)指令。應(yīng)用單獨的任務(wù),則在編譯器工作的同時解釋器立即恢復(fù)執(zhí)行指令。由單獨任務(wù)完成的工作總量是增大了,因為它們必須發(fā)送和接收翻譯請求,但較小的延遲意味著在編譯器工作時系統(tǒng)的使用者觀察不到暫停。再有,在編譯器工作時解釋器任務(wù)能響應(yīng)外部事件,如中斷,這在組合任務(wù)安排中是不可能的。在實踐中,在組合安排中編譯器受到編譯器的延時這一事實對編譯器的復(fù)雜性和翻譯后代碼質(zhì)量是一個限制。例如,JavaJust-In-Time編譯器應(yīng)該執(zhí)行得足夠快。以使與Java系統(tǒng)進行交互作用的使用者看不到暫停,這種情況禁止了某些復(fù)雜的優(yōu)化。類似地,OCT系統(tǒng)只在單條翻譯后指令內(nèi)進行優(yōu)化,以減少編譯時間。單獨的編譯器任務(wù)允許穿過多條指令進行優(yōu)化。單獨編譯器任務(wù)的第三個優(yōu)點是在解釋器任務(wù)和編譯器任務(wù)中出現(xiàn)的故障是彼此隔離的。這意味著如果編譯器任務(wù)得到一個地址例外或其他例外狀況,解釋器任務(wù)不受影響。編譯器在故障之后對本身復(fù)位,并繼續(xù)對下一個請求工作。因為解釋器不等待編譯器結(jié)束一個翻譯請求,所以它們并不注意編譯器是否有了故障。單獨編譯器任務(wù)的第四個好處是它能平衡編譯器和解釋器任務(wù)的負(fù)擔(dān)。在動態(tài)翻譯系統(tǒng)中,有時解釋器任務(wù)很忙并需要全部計算機CPU,而有時解釋器任務(wù)空閑以至CPU不被使用。在組合的解釋器和編譯器安排中,大多數(shù)編譯工作是在解釋器忙的時候做的,因為只有當(dāng)解釋器在運行時編譯器才被調(diào)用。這沒有利用空間的CPU周期。在單獨的編譯器任務(wù)安排中,當(dāng)解釋器空閑時編譯器繼續(xù)工作。它產(chǎn)生編譯器將來可能使用的翻譯后代碼。第三實施例的特別目的本發(fā)明第三實施例的目的是與具有多個物理執(zhí)行單元的系統(tǒng)上執(zhí)行的多個解釋器并發(fā)地使用動態(tài)翻譯,從而提供較小的可執(zhí)行圖像大小,減小的高速緩存競爭,較低的解釋器執(zhí)行延時,故障隔離以及更好的負(fù)擔(dān)平衡。第三實施例摘要所描述的動態(tài)翻譯系統(tǒng)與被解釋程序的執(zhí)行并發(fā)地完成動態(tài)翻譯。該系統(tǒng)使用單獨的編譯器,從而使它不會顯著地影響解釋器任務(wù)的性能。本發(fā)明與具有多個物理執(zhí)行單元的系統(tǒng)上執(zhí)行的多個解釋器并發(fā)地使用動態(tài)翻譯,從而提供較小的可執(zhí)行圖像大小,減小的高速緩存競爭,較低的解釋器執(zhí)行延時,故障隔離以及更好的負(fù)擔(dān)平衡。本發(fā)明的第四實施例動態(tài)翻譯過程中仿真以減輕對仿真器進行記錄的負(fù)擔(dān)第四實施例概要結(jié)構(gòu)仿真是用一個不同的計算機結(jié)構(gòu)對一計算機結(jié)構(gòu)的嚴(yán)格模仿,從而使原始結(jié)構(gòu)的機器碼能不經(jīng)修改地運行。目標(biāo)碼翻譯是把一種計算機結(jié)構(gòu)的機器碼翻譯成另一種計算機結(jié)構(gòu)的機器碼的過程。所描述的動態(tài)優(yōu)化目標(biāo)碼翻譯系統(tǒng)使用編譯器優(yōu)化技術(shù)達到比用于結(jié)構(gòu)仿真的基于模板的目標(biāo)碼翻譯更好的性能。然而,它需要進行運行概況記錄以實現(xiàn)動態(tài)優(yōu)化目標(biāo)碼翻譯。本描述解釋一種減輕概況記錄負(fù)擔(dān)的方法。第四實施例附圖描述圖26說明一個根據(jù)本發(fā)明第四實施例構(gòu)成的翻譯表,它用于記錄哪些指令是可翻譯的。哪些指令是不可翻譯的。圖26是一個翻譯表,顯示出哪些程序是可翻譯的,哪些是不可翻譯的。在這種情況中,程序以I字節(jié)為單位測量。仿真器檢驗一個分支后繼者對應(yīng)于哪個入口條目,從而確定該分支是否跳到一個可翻譯的程序。圖27說明根據(jù)本發(fā)明第四實施例如何減輕仿真器上概況記錄的負(fù)擔(dān)。圖27是一個流程圖,它顯示仿真器怎樣對可翻譯程序啟動記錄過程而對不可翻譯的程序關(guān)掉記錄過程。觸發(fā)器*1和觸發(fā)器*2指令二者都應(yīng)被記錄,但觸發(fā)器*1指令不可以在可翻譯程序和不可翻譯程序之間轉(zhuǎn)跳。只有觸發(fā)器*2指令能在它們之間轉(zhuǎn)跳。日志標(biāo)志記住該仿真器是在一可翻譯程序中運行還是在不可翻譯程序中運行。所以,在觸發(fā)器*1指令中,仿真器不必檢驗翻譯表或改變?nèi)罩緲?biāo)志。它只是檢驗分支后繼指令是否已被編譯,并立即跳到編譯后代碼。因為觸發(fā)器*1指令代表最頻繁執(zhí)行的觸發(fā)器指令,所以這一算法能減輕對仿真進行概況記錄的負(fù)擔(dān)。第四實施例詳述動態(tài)優(yōu)化目標(biāo)碼翻譯通過產(chǎn)生更快的指令而實現(xiàn)了高性能,但它引起存儲器和時間的耗費。所以,在結(jié)構(gòu)仿真時,動態(tài)優(yōu)化目標(biāo)碼翻譯和仿真二者在一起使用。翻譯被用于頻繁運行和需要高性能的主要程序。而仿真器為小程序工作,而且還記錄主要程序直至翻譯器完成編譯。一個概況記錄被翻譯器用于編譯和優(yōu)化程序??赡軓奈幢环g代碼跳到被翻譯代碼的指令叫做觸發(fā)器指令。如果一個觸發(fā)器指令能從一個小程序跳到一個主要程序,或者能從一個主要程序跳到一個小程序,則稱它為觸發(fā)器*2指令。如果它只能在一個小程序內(nèi)轉(zhuǎn)跳,或只能在一個主要程序內(nèi)轉(zhuǎn)跳,則稱它為觸發(fā)器*1指令。因為翻譯器不對小程序進行工作,所以不必要對主要程序中的觸發(fā)器*1指令進行記錄,因為該程序的一部分可能已被翻譯而另一部分尚未被翻譯。有必要對小程序和主要程序中的觸發(fā)器*2指令進行記錄,因為它們可能會跳到一個主要程序中。在執(zhí)行一個觸發(fā)器*2指令之后,仿真完成三個檢驗(見圖27)。第一,它檢驗該翻譯器是否起動。如果它已起動,它檢驗該觸發(fā)器*2指令的后繼者是不是可翻譯的。如果它是可翻譯的,則由仿真器設(shè)置日志記錄標(biāo)志為真,并檢驗其后繼者是否已被翻譯,如果存在翻譯后版本的話,則跳到該翻譯后版本。在執(zhí)行一個觸發(fā)器*2指令之后,仿真只完成二個檢驗(見圖27)。第一,它檢驗日志記錄標(biāo)志是啟動還是斷掉。如果該標(biāo)志是斷掉,則該指令是在一個小程序中,而且它不需要被記錄,如果該標(biāo)志是啟動,則仿真檢驗它的后的繼者是否被翻譯。主要程序和小程序是由它們的存儲器地址區(qū)分的(見圖26)。仿真器使用一個翻譯表來記錄可翻譯和不可翻譯程序地址的關(guān)系。對于觸發(fā)器*1指令,它們從不在可翻譯程序和不可翻譯程序之間運動,仿真器不必訪問翻譯表,因為日志記錄標(biāo)志已經(jīng)包含了那個信息。通過把對于觸發(fā)器*1和觸發(fā)器*2指令的仿真器行為分解成兩種方法,使對仿真進行概況記錄的負(fù)擔(dān)被減輕了。第四實施例的特別目的本發(fā)明第四實施例的目的是實現(xiàn)減輕仿真器上概況記錄負(fù)擔(dān)的方法,其作法是在能跳進或跳出可翻譯指令的觸發(fā)器指令之后放置代碼以檢驗該分支后繼者是否是可翻譯的,以及在所有其他觸發(fā)器之后放置代碼只檢驗一個標(biāo)志,看其是否是可翻譯的。第四實施例摘要把動態(tài)目標(biāo)碼翻譯與仿真一起使用是有效的,但記錄指令概況以指導(dǎo)翻譯器的費用是加在仿真上的一個負(fù)擔(dān)。通過區(qū)分被記錄指令的不同類型,有可能減輕這種負(fù)擔(dān)。本發(fā)明是減輕仿真器上概況記錄負(fù)擔(dān)的方法,其作法是在能跳進或跳出可翻譯指令的觸發(fā)器指令之后放置代碼以檢驗該分支后繼者是否是可翻譯的,以及在所有其他觸發(fā)器之后放置代碼只檢驗一個標(biāo)志,看其是否是可翻譯的。本發(fā)明第五實施例動態(tài)翻譯的軟件反饋第五實施例概要動態(tài)翻譯是在程序運行過程中把一種機器語言的計算機程序翻譯成另一種機器語言的活動。在一些動態(tài)翻譯系統(tǒng)中,運行該程序的任務(wù)(稱作解釋器)與翻譯該程序的任務(wù)(稱作編譯器)是分開的。解釋器向編譯器發(fā)送請求的速率應(yīng)與編譯器完成請求的速率匹配。還有解釋器發(fā)送請求的速率不應(yīng)降到零。軟件反饋提供了使這兩種速率趨于相等的途徑。第五實施例附圖描述圖28是根據(jù)本發(fā)明第五實施例構(gòu)成的帶有單獨解釋器和編譯器的動態(tài)翻譯系統(tǒng)的總體結(jié)構(gòu)圖。圖28是一個動態(tài)翻譯系統(tǒng)的結(jié)構(gòu)圖。解釋器向編譯器發(fā)送翻譯請求。編譯器發(fā)送回翻譯后代碼作為響應(yīng)。請求和響應(yīng)的速率應(yīng)用相等以使該系統(tǒng)運行得最有效。圖29說明根據(jù)本發(fā)明第五實施例的軟件反饋機制的組成部分。圖29是說明一軟件反饋系統(tǒng)組成部分的示意圖。比較過程從請求數(shù)中減去完成數(shù)。請求速率過程根據(jù)這一差值設(shè)定其速率。請求發(fā)送過程根據(jù)當(dāng)前速率發(fā)送請求。第五實施例詳述在一動態(tài)翻譯系統(tǒng)中,解釋器任務(wù)向編譯器任務(wù)發(fā)送請求。這請求包括要告知編譯器的信息,即程序的哪部分要翻譯。編譯器翻譯該部分并以翻譯后代碼作為響應(yīng)。決定何時發(fā)送請求的問題是調(diào)度問題的一個實例。解釋器任務(wù)做出請求的速率應(yīng)與編譯器完成請求的速率匹配。所以編譯器將不會空閑或因請求而超載。軟件以饋是使兩組事件速率相等的一種方法[1]。在動態(tài)翻譯系統(tǒng)中,它改變翻譯請求的速率使其等于完成翻譯的速率。如圖29所示,軟件反饋系統(tǒng)有三個主要部分。第一部分是一個過程,用于比較翻譯請求次數(shù)和完成翻譯次數(shù)。第二部分是一個過程,用于根據(jù)比較結(jié)果去改變翻譯請求速率。第三部分是一個過程,用于根據(jù)第二過程的輸出做出翻譯請求。在動態(tài)翻譯系統(tǒng)中,解釋器任務(wù)對一分支指令跳到一特定目的地地址的頻繁程度進行計數(shù)。當(dāng)這一計數(shù)超過一閾值時,解釋器發(fā)送一個包含這目的地地址的請求。這個閾值是由軟件反饋機制設(shè)定的關(guān)鍵性參數(shù)。當(dāng)這閾值低于大多數(shù)執(zhí)行計數(shù)時,翻譯請求速率是高的。當(dāng)這閾值高于大多數(shù)執(zhí)行計數(shù)時,這請求速率是低的。因為執(zhí)行計數(shù)的典型大小是隨被解釋的程序而變的,所以軟件反饋是設(shè)定閾值的理想方式,因為它自動地適應(yīng)于解釋器的行為。在動態(tài)翻譯系統(tǒng)中,軟件反饋的比較過程是很簡單的。它只是計算發(fā)送給編譯器的翻譯請求的次數(shù)與完成的翻譯次數(shù)之差。請求速率過程根據(jù)比較過程計算出的差值改變閾值。如果差值為零,則閾值太高,于是它阻止解釋器發(fā)送翻譯請求。在那種情況中,請求速率過程從閾值中減去一個常數(shù)。如果差值是它的最大可能值,則閾值太低,解釋器發(fā)送太多翻譯請求。在那種情況中,請求速率過程向閾值加一個常數(shù)。請求發(fā)送過程是在解釋器執(zhí)行一分支指令時被調(diào)用的。如果該分支指令已跳到同一目的地地址的次數(shù)多于閾值,則解釋器發(fā)送一個包括該目的地地址的翻譯請求。第五實施例的特別目的本發(fā)明是在具有單獨解釋器任務(wù)和編譯器任務(wù)的動態(tài)翻譯系統(tǒng)中使用一種軟件反饋機制,以使解釋器發(fā)送翻譯請求的速率等于編譯器完成翻譯的速率,不允許編譯器變成空閑的。使用最小閾值以允許編譯器關(guān)閉。第五實施例摘要在一個具有單獨的解釋器任務(wù)和編譯器任務(wù)的動態(tài)翻譯系統(tǒng)中,解釋器發(fā)送請求的速率應(yīng)與編譯器完成這些請求的速率相匹配。還有,解釋器發(fā)送請求的速率不應(yīng)降至零。本發(fā)明在具有單獨的解釋器任務(wù)和編譯器任務(wù)的動態(tài)翻譯系統(tǒng)中使用一種軟件反饋機制,以使解釋器發(fā)送翻譯請求的速率等于編譯器完成翻譯的速率,不允許編譯器變成空閑的。本發(fā)明第六實施例動態(tài)翻譯請求排隊第六實施例概要動態(tài)翻譯是在程序運行過程中把一種機器語言的計算機程序翻譯成另一種機器語言的活動。對于每片被翻譯的程序,該系統(tǒng)做成向動態(tài)翻譯器的一個請求。當(dāng)動態(tài)翻譯器忙時造成的請求被排隊并在翻譯器變?yōu)榭臻e時發(fā)出。排隊的實現(xiàn)把系統(tǒng)調(diào)用與共享存儲器通信組合起來,以減小它。第六實施例附圖描述圖30說明根據(jù)本發(fā)明第六實施例如何在翻譯任務(wù)忙時使用一個隊列來容納翻譯請求。圖31說明根據(jù)本發(fā)明第六實施例OOCT請求隊列如何把便宜的共享存儲器請求與系統(tǒng)調(diào)用請求組合起來。第六實施例詳述如圖30所示,請求隊列的基本功能是記憶動態(tài)翻譯器忙時做出的請求。在任何動態(tài)翻譯系統(tǒng)中,都有一個能同時發(fā)生的翻譯次數(shù)的上限。通常這個限度是一個時候只有一個翻譯。然而,對于造成的請求總數(shù)或造成請求的速率,卻沒有限制。所以很有可能當(dāng)翻譯器已經(jīng)忙時又發(fā)生一個翻譯請求。利用請求隊列,翻譯請求被放入一個隊列,于是不需要重復(fù)該請求。當(dāng)翻譯器把該請求從隊列中取出時,它將完成翻譯。在OOCT中,動態(tài)翻譯系統(tǒng)有多重任務(wù),一個是處置請求的動態(tài)翻譯任務(wù),其他是造成翻譯請求的執(zhí)行任務(wù)。如圖31所示,OOCT的隊列實現(xiàn)使用不昂貴的共享存儲器與系統(tǒng)調(diào)用消息結(jié)合,構(gòu)成請求隊列,從而在一個樸素的隊列上得到改善。單是系統(tǒng)調(diào)用便足以把種子從執(zhí)行任務(wù)傳送到翻譯任務(wù),并在沒有待決請求時允許翻譯任務(wù)變?yōu)榭臻e或鎖住。然而,系統(tǒng)調(diào)用是昂貴的操作。共享存儲器能被用于把請求消息從執(zhí)行任務(wù)傳送到翻譯任務(wù),但翻譯任務(wù)不能鎖住那些消息,所以它得要連續(xù)地運行以從簡單的共享存儲器隊列中接收消息。OOCT實現(xiàn)使用系統(tǒng)調(diào)用和共享存儲器這兩種機制每個所具有的最好特性。它允許翻譯任務(wù)鎖住以等待系統(tǒng)調(diào)用消息,但當(dāng)翻譯任務(wù)已經(jīng)在工作時,則通過共享存儲器傳送請求。如圖31所示,OOCT請求隊列在執(zhí)行任務(wù)和翻譯任務(wù)之間使用兩類消息,加上由這兩種任務(wù)訪問的共享存儲器緩沖器。第一類消息從翻譯任務(wù)去到執(zhí)行任務(wù)。它告訴執(zhí)行任務(wù)使用系統(tǒng)調(diào)用去發(fā)送下一個請求。這一消息通知執(zhí)行任務(wù)翻譯任務(wù)已使共享存儲器緩沖器變空并且即將要鎖住。然后執(zhí)行任務(wù)以系統(tǒng)調(diào)用發(fā)送請求。翻譯任務(wù)接收這消息并開始翻譯。在以系統(tǒng)調(diào)用發(fā)送一個請求之后,執(zhí)行任務(wù)知道翻譯任務(wù)在忙,于是它直接向共享存儲器緩沖器發(fā)送更多的請求。這比使用另一個系統(tǒng)調(diào)用要便宜得多。當(dāng)翻譯任務(wù)結(jié)束一個請求時,它查看共享存儲器緩沖器。如果在緩沖器中有一個請求,則它被移走并被翻譯。當(dāng)共享存儲器緩沖器空時,翻譯任務(wù)再次告訴執(zhí)行任務(wù)去使用系統(tǒng)調(diào)用。OOCT請求隊列的好處是當(dāng)執(zhí)行任務(wù)以高速率發(fā)送請求時,它們能使用共享存儲器緩沖器,而當(dāng)請求以低速率到來時,翻譯任務(wù)能鎖住。第六實施例的特別目的本權(quán)利要求是日文的富士通(Fujitsu)專利的譯文,加了一個條款。本發(fā)明是當(dāng)通過向翻譯任務(wù)發(fā)送一個消息從而開始翻譯被分支頻繁轉(zhuǎn)到的指令時使解釋工作能繼續(xù)進行的一種方法,也是當(dāng)翻譯已在進行之中時使發(fā)給翻譯任務(wù)的消息排成隊列的方法;本發(fā)明由使用系統(tǒng)調(diào)用和共享存儲器兩種機制發(fā)送翻譯請求消息改善了性能。第六實施例摘要所描述的翻譯請求隊列是另一個翻譯在執(zhí)行時收集翻譯請求的一種機制。它允許執(zhí)行任務(wù)在發(fā)送一個請求之后立即繼續(xù)運行。通過一起使用共享存儲器和系統(tǒng)調(diào)用,使得有可能改善翻譯隊列的有效性。本發(fā)明是當(dāng)通過向翻譯任務(wù)發(fā)送一個消息從而開始翻譯被分支頻繁轉(zhuǎn)到的指令時使解釋工作能繼續(xù)進行的一種方法,也是當(dāng)翻譯已在進行之中時使發(fā)給翻譯任務(wù)的消息排成隊列的方法;本發(fā)明由使用系統(tǒng)調(diào)用和共享存儲器兩種機制發(fā)送翻譯請求消息改善了性能。本發(fā)明第七實施例動態(tài)翻譯的頁面錯恢復(fù)第七實施例概要動態(tài)翻譯是在程序運行過程中把一種機器語言的計算機程序翻譯成另一種機器語言的活動。動態(tài)翻譯器在把源機器指令翻譯成目標(biāo)機器指令之前必須先讀出這些源機器指令。在讀這些源指令過程中,翻譯器由于從頁面外存儲器中讀出能造成頁面錯,但在存儲器內(nèi)分頁是效率低的。所描述的翻譯器能從頁面錯中恢復(fù)而無需讀頁面外數(shù)據(jù)并繼續(xù)進行翻譯。第七實施例附圖描述圖32顯示根據(jù)本發(fā)明第七實施例一個動態(tài)翻譯器如何能引起頁面錯,而這些頁面錯在正常執(zhí)行源指令時本不會發(fā)生的。圖33顯示根據(jù)本發(fā)明的第七實施例,在翻譯過程中從頁面錯中恢復(fù)并繼續(xù)翻譯的算法。第七實施例詳述動態(tài)翻譯器很可能訪問一些頁面,而這些頁面是用于復(fù)制到物理存儲器中的壞候選者,因為它讀出了一條指令的所有可能的后繼者,而不只是實際要被執(zhí)行的后繼者。例如,如圖32中所示,條件分支指令有兩個后繼者,即分支歸于失敗的后繼者和分支被采取時的后繼者。當(dāng)CPU執(zhí)行一條件分支指令時,如果該分支未被采取,則分支被采取時的后繼者決不會被加載。所以它將不會引起頁面錯。當(dāng)動態(tài)翻譯器讀這個分支指令時,它試圖讀出分支歸于失敗的后繼者和分支被采取時的后繼者這兩個后繼者,不知道哪個將被實際執(zhí)行。讀分支后繼者可能引起頁面錯,即使該后繼者將永不會被執(zhí)行。處置頁面錯的通常方法是分頁到請求的存儲器中和在軟件中完成存儲器訪問,然后允許在出錯指令之后繼續(xù)執(zhí)行。這一方法有兩種消耗,第一,把一頁從物理存儲器移到后備存儲,再把另一頁從后備存儲移到物理存儲器,然后完成存儲器訪問,這些是費時間的。第二,它改變了被分頁進來的存儲器頁面集合。被復(fù)制到物理存儲器中的頁面可能在它再次被分頁出去之前并未被頻繁訪問,這就表明把它復(fù)制到物理存儲器中不是一個好的想法。由于動態(tài)翻譯器能造成更頻繁的頁面錯,所以降低那些頁面錯的費用是有利的。動態(tài)翻譯器通過不把新頁復(fù)制到物理存儲器和不把已在物理存儲器中的頁面驅(qū)逐出去的辦法使額外頁面錯的費用減至最小。這節(jié)省了復(fù)制時間并且還保證不經(jīng)常被訪問的頁面不被復(fù)制進去。不去復(fù)制該頁,代之以由頁面錯處理器中斷翻譯器中的當(dāng)前指令流,并把控制返回到由翻譯器指定的檢驗點。翻譯器以基本塊為單元讀源指令。如果在讀一個基本塊時發(fā)生了頁面錯,則翻譯器忽略那個塊但繼續(xù)翻譯任何其他塊。在所有基本塊讀完之后,它們被翻譯成一組目標(biāo)指令。忽略引起頁面錯的基本塊的方法示于圖33。在讀一個基本塊之前,翻譯器做成一個檢驗點。在此檢驗點之前被讀出的所有基本塊是安全的,不會受到該檢驗點之后發(fā)生的任何頁面錯的影響。然后翻譯器試讀下一個基本塊。如果有一個頁面錯,它便立即跳到該檢驗點。這使它跳過該基本塊并試讀下一個基本塊。第七實施例的特別目的根據(jù)第七實施例的發(fā)明是降低動態(tài)翻譯的存儲器訪問費用的一種途徑,其作法是當(dāng)一存儲器訪問失敗時不把頁面復(fù)制到物理存儲器中但仍然允許繼續(xù)進行翻譯。第七實施例摘要所描述的頁面錯恢復(fù)機制是當(dāng)訪問非物理映射存儲器時降低動態(tài)翻譯費用的一種途徑。它允許動態(tài)翻譯即使在由于頁面錯而不能讀出全部源機器指令時也繼續(xù)工作。本發(fā)明是降低動態(tài)翻譯的存儲器訪問費用的一種途徑,其作法是當(dāng)一存儲器訪問失敗時不把頁面復(fù)制到物理存儲器中但仍然允許繼續(xù)進行翻譯。本發(fā)明第八實施例為動態(tài)翻譯記錄從翻譯后代碼的退出第八實施例概要動態(tài)翻譯是在程序運行過程中把一種機器語言的計算機程序翻譯成另一種機器語言的活動。動態(tài)翻譯器通過在指令執(zhí)行過程中概要記錄它們來選擇要翻譯的指令。頻繁執(zhí)行的指令被翻譯,而不頻繁執(zhí)行的指令則不被翻譯。翻譯后的指令能使概要記錄器丟掉一些指令,這可能會使頻繁執(zhí)行的指令要被解釋。通過記錄從翻譯后代碼的具體退出情況,有可能概要記錄全部頻繁執(zhí)行的指令和保證它們?nèi)急环g。第八實施例附圖描述圖34說明根據(jù)本發(fā)明第八實施例構(gòu)成的帶有分支概要記錄器的動態(tài)翻譯系統(tǒng)中的控制流程模式。第八實施例詳述如在報告動態(tài)翻譯用分支記錄器中描述的那樣,動態(tài)翻譯系統(tǒng)在原始程序的分支指令被解釋的過程中概要記錄它們,以確定哪些指令是頻繁執(zhí)行的,哪些指令不是。分支記錄器只概要記錄分支指令,并依據(jù)這樣一個假定,即所有頻繁執(zhí)行的指令都是通過頻繁執(zhí)行的分支來達到的。在一些情況中,動態(tài)翻譯器本身使這一假定不成立,因為控制將從翻譯后代碼流回被解釋的指令而不去執(zhí)行一個被概要記錄的分支。翻譯器能識別那些情況,而且它創(chuàng)建特殊的翻譯后指令,這些指令概要記錄這個控制流,就如同這個控制流是一個分支。圖34說明控制流如何從被解釋指令到翻譯后的指令以及又如何回去。每當(dāng)控制從翻譯后指令中退出時,翻譯器要確保該退出被概要記錄下來,就如同它是一個分支指令。有幾種情況中控制流是從翻譯后指令到被解釋指令。第一,存在到非固定目的地的分支。翻譯器不知道在此分支之后將執(zhí)行哪條指令,所以它不能把那條指令作為分支結(jié)合到這同一翻譯單元中。它代之以創(chuàng)建一個從翻譯后指令回到被解釋代碼的退出。第二,由于翻譯過程中的頁面錯,存在不能被讀出的指令。如在報告‘動態(tài)翻譯的頁面錯恢復(fù)’中描述的那樣,翻譯器忽略因頁面錯而不能讀出的指令塊。所以當(dāng)翻譯后的程序達到那些塊時不得不跳回被解釋的指令。第三,當(dāng)進行翻譯時,某些指令被頻繁地執(zhí)行。如在報告‘動態(tài)翻譯的塊檢出閾值’中描述的那樣,它們沒有因為曾經(jīng)頻繁執(zhí)行而被翻譯。但它們在將來可能變?yōu)轭l繁執(zhí)行的,所以翻譯器必須記錄退出到那些指令的情況。這一特性使動態(tài)翻譯系統(tǒng)能適應(yīng)于改變執(zhí)行模式,這些模式改變頻繁執(zhí)行的指令的分布。因為從翻譯后代碼的退出被記錄下來,所以有更多的指令被翻譯。這增加了一條指令的翻譯后版本存在的機會。所以,在長時間運行這動態(tài)翻譯系統(tǒng)之后,大多數(shù)從翻譯后單元的退出會造成轉(zhuǎn)跳到另一個翻譯后單元,而不是跳回到被解釋的代碼。這從更經(jīng)常地使用較快的翻譯后指令當(dāng)中得到直接益處,而從不那么經(jīng)常地執(zhí)行分支記錄指令中得到間接益處。第八實施例的特別目的本發(fā)明的第八實施例的目的是提供一種方法,通過概要記錄翻譯后指令單元的可能退出情況確保頻繁執(zhí)行的指令被翻譯,即使通過任何被記錄的分支都不能達到它們。第八實施例摘要一個動態(tài)翻譯系統(tǒng)必須找到和翻譯所有頻繁執(zhí)行的指令,這可由概要記錄分支指令來實現(xiàn)。但翻譯指令的過程會造成到達那些不包括被記錄分支的指令。所以,概要記錄被擴展到包括從翻譯后指令的退出。本發(fā)明是一種方法,通過概要記錄翻譯后指令單元的可能退出情況確保頻繁執(zhí)行的指令被翻譯,即使通過任何被記錄的分支都不能達到它們。本發(fā)明第九實施例動態(tài)翻譯的塊檢出閾值第九實施例概要動態(tài)翻譯是在程序運行過程中把一種機器語言的計算機程序翻譯成另一種機器語言的活動。動態(tài)翻譯器應(yīng)翻譯源程序中所有頻繁執(zhí)行的部分而忽略所有不頻繁執(zhí)行的部分。為實現(xiàn)這一點,翻譯系統(tǒng)概要記錄分支指令,并且不去翻譯那些執(zhí)行概率低于指定閾值的指令。第九實施例附圖描述圖35說明根據(jù)本發(fā)明第九實施例該動態(tài)翻譯器如何使用概要記錄信息去計算一個基本塊的執(zhí)行概率。第九實施例詳述一個動態(tài)翻譯器的目的是通過把一計算機程序由其原始的源語言指令翻譯成更有效的目標(biāo)語言指令從而該計算機程序的總體執(zhí)行速度。動態(tài)翻譯的益處是通過把執(zhí)行原始程序的總時間與翻譯該程序所需時間加上執(zhí)行翻譯后程序的時間進行比較來度量的。翻譯程序中任何部分所需時間近似為常數(shù),所以翻譯一部分所獲得的益處主要決定于使用那一部分的次數(shù)。被頻繁執(zhí)行的指令是值得翻譯的,但不被頻繁執(zhí)行的指令則不值得翻譯。為了測量不同指令的頻度,動態(tài)翻譯系統(tǒng)可概要記錄分支指令。利用這個概要記錄信息,它能檢出一個被頻繁執(zhí)行的指令并在那一點處開始翻譯。在初始指令之后,翻譯器盡可能多地讀出被頻繁執(zhí)行的后繼指令而不去讀不頻繁的后繼者。塊檢出閾值被用于確定一個后繼者是被頻繁執(zhí)行的還是不被頻繁執(zhí)行的。動態(tài)翻譯器按稱作基本塊的單元讀出指令。在一個基本塊中,所有指令被執(zhí)行相同次數(shù),所以它們或者全是被頻繁執(zhí)行的,或者全是不被頻繁執(zhí)行的。動態(tài)翻譯器使用來自分支指令的概要記錄信息以確定一個基本塊是被頻繁執(zhí)行的還是不被頻繁執(zhí)行的。這一過程示于圖35。翻譯器計算從第一條被翻譯指令到一給定基本塊的執(zhí)行路徑被采取的概率。給第一基本塊的執(zhí)行概率是100%,因為它包含該第一條指令。如果當(dāng)前塊只有一個后繼者,那么該后繼者的執(zhí)行概率與當(dāng)前塊的執(zhí)行概率相同。如果當(dāng)前塊結(jié)束于一個條件分支,則根據(jù)分支概要記錄信息將當(dāng)前塊的概率在這兩個后繼者之間分配。例如,如果當(dāng)前塊的執(zhí)行概率是50%,它結(jié)束于一個分支指令,該分支指令被執(zhí)行了40次,其中被采取了10次,于是被采取分支后繼者的概率為(50%×25%=12.5%),而分支歸于失敗的后繼者的概率是(50%×75%=37.5%)。使用了一個稱作塊檢取閾值的可變閾值,用于選擇被頻繁執(zhí)行的塊。如果一塊的執(zhí)行概率大于或等于該閾值,則認(rèn)為那一塊是被頻繁執(zhí)行的,于是將其翻譯。如果執(zhí)行概率低于該閾值,則認(rèn)為該塊是不被頻繁執(zhí)行的,于是不翻譯它。這種塊檢出方法的一個重要性質(zhì)是被檢出的一組塊是連接的。有一些更復(fù)雜的方式去計算執(zhí)行概率,例如把所有先行指令的概率相加。但這會導(dǎo)致不連接的塊組。翻譯不連接的塊組是可能的,但是,如果塊組是連接的,則有更多的機會去優(yōu)化翻譯后的代碼。第九實施例的特別目的本發(fā)明第九實施例的目的是提供一種改善動態(tài)翻譯效率的方法,其作法是使用一個閾值執(zhí)行概率把被頻率執(zhí)行的指令塊與不被頻繁執(zhí)行的指令塊區(qū)分開,從而選擇被頻繁執(zhí)行的指令塊供翻譯和忽略不被頻繁執(zhí)行的指令塊。第九實施例摘要一個動態(tài)翻譯系統(tǒng)的耗費正比于被翻譯指令數(shù),而其益處正比于翻譯后指令的被執(zhí)行次數(shù)。所以最有效的作法是只翻譯被頻繁執(zhí)行的指令而忽略不被頻繁執(zhí)行的指令。本發(fā)明是改善動態(tài)翻譯效率的一種方法,其作法是使用一個閾值執(zhí)行概率把頻繁執(zhí)行的指令塊與不被頻繁執(zhí)行的指令塊區(qū)分開,從而選擇被頻率執(zhí)行的指令塊供翻譯和忽略不被頻繁執(zhí)行的指令塊。雖然已經(jīng)圖示和描述了本發(fā)明的一些最佳實施例,但本領(lǐng)域的技術(shù)人員應(yīng)該理解,在這些實施例中可以做出改變而不離開本發(fā)明的原理和精神,本發(fā)明的范圍在權(quán)利要求及其等效物中規(guī)定。權(quán)利要求1.在目的計算機結(jié)構(gòu)上仿真源計算機結(jié)構(gòu)的一個計算機結(jié)構(gòu)仿真系統(tǒng),包括解釋器,用于把源目標(biāo)碼單個地翻譯成相應(yīng)的翻譯后目標(biāo)碼并確定在源目標(biāo)碼中分支指令的執(zhí)行次數(shù);以及編譯器,用于當(dāng)一相應(yīng)的分支指令執(zhí)行次數(shù)超過一閾值數(shù)時把源目標(biāo)碼指令組合成段并動態(tài)地編譯該段。2.根據(jù)權(quán)利要求1的計算機結(jié)構(gòu)仿真系統(tǒng),這里對應(yīng)于未被編譯段的分支指令存儲在存儲器中。3.根據(jù)權(quán)利要求2的計算機結(jié)構(gòu)仿真系統(tǒng),這里尚未超過閾值數(shù)的分支指令所對應(yīng)的段不被編譯。4.根據(jù)權(quán)利要求1的計算機結(jié)構(gòu)仿真系統(tǒng),這里在所述解釋器執(zhí)行翻譯后目標(biāo)碼指令過程中,對應(yīng)于未被編譯的分支指令的段被存儲在存儲器中。5.根據(jù)權(quán)利要求1的計算機結(jié)構(gòu)仿真系統(tǒng),這里所述解釋器和所述編譯器是同時在一多任務(wù)操作系統(tǒng)中實時運行的任務(wù)。6.根據(jù)權(quán)利要求1的計算機結(jié)構(gòu)仿真系統(tǒng),進一步包括分支記錄器,用于存儲由所述解釋器確定的分支指令分支概要分析信息。7.根據(jù)權(quán)利要求6的計算機結(jié)構(gòu)仿真系統(tǒng),這里所述分支概要分析信息包括分支地址、分支后繼者、不分支后繼者、分支執(zhí)行計數(shù)和分支被采取計數(shù),而且所述分支概要分析信息是在分支指令仿真過程中由所述解釋器記錄。8.根據(jù)權(quán)利要求1的計算機結(jié)構(gòu)仿真系統(tǒng),其中在執(zhí)行跳入或跳出可翻譯指令的分支指令之后放置代碼標(biāo)志;以及通過參考相應(yīng)代碼標(biāo)志來檢驗對應(yīng)于分支指令的后繼指令是否可被翻譯。9.根據(jù)權(quán)利要求1的計算機結(jié)構(gòu)仿真系統(tǒng),其中在分支指令的一后繼指令執(zhí)行次數(shù)超過一相應(yīng)閾值時啟動翻譯該分支指令。10.根據(jù)權(quán)利要求1的計算機結(jié)構(gòu)仿真系統(tǒng),其中在所述解釋器繼續(xù)仿真源代碼的同時,在所述解釋器和所述編譯器之間進行通信,通過這種通信啟動對頻繁被分支的指令所對應(yīng)段進行翻譯。11.根據(jù)權(quán)利要求1的計算機結(jié)構(gòu)仿真系統(tǒng),其中當(dāng)存儲要被翻譯段的隊列達到一預(yù)先確定的容量時,通過提高閾值數(shù)來控制要被編譯段的編譯速率。12.根據(jù)權(quán)利要求1的計算機結(jié)構(gòu)仿真系統(tǒng),這里所述編譯器在按順序追蹤存儲器中每條指令時,使用對應(yīng)于編譯起點地址的概要分析信息建立一個優(yōu)化的目標(biāo)。13.根據(jù)權(quán)利要求12的計算機結(jié)構(gòu)仿真系統(tǒng),這里在檢測到頁面錯時所述編譯器不去編譯該塊,而是當(dāng)一塊引起頁面錯時,所述編譯器在所述分支記錄器中產(chǎn)生一個記錄分支信息的目標(biāo)。14.根據(jù)權(quán)利要求13的計算機結(jié)構(gòu)仿真系統(tǒng),這里如果一個指令執(zhí)行過程沒能以預(yù)先確定的速率及時地執(zhí)行,則所述編譯器利用概要分析信息追蹤其執(zhí)行情況,檢驗分支計數(shù)是否在一預(yù)先確定數(shù)之下,并產(chǎn)生記錄分支信息的一個目標(biāo)。15.根據(jù)權(quán)利要求1的計算機結(jié)構(gòu)仿真系統(tǒng),進一步包括分支記錄器,用于存儲源目標(biāo)碼中分支指令的概要分析信息,其中包括執(zhí)行次數(shù),這里所述分支記錄器包括一個存儲被頻繁執(zhí)行的分支指令概要分析信息的高速緩存和一個存儲不被頻繁執(zhí)行的分支指令概要分析信息的分支日志。16.根據(jù)權(quán)利要求15的計算機結(jié)構(gòu)仿真系統(tǒng),這里通過使分支地址信息和分支目的地信息組合,來把概要分析信息組織在高速緩存中。17.根據(jù)權(quán)利要求16的計算機結(jié)構(gòu)仿真系統(tǒng),這里組織在高速緩存中的概要分析信息存儲在多個組中,每個組是按照進入各組的概要分析信息的入口條目遞減順序來組織的。18.根據(jù)權(quán)利要求1的計算機結(jié)構(gòu)仿真系統(tǒng),這里每個分支指令是一個種子,所述編譯器進一步包括塊檢出器,它根據(jù)該種子和該分支的概要分析信息來選擇要被編譯的源目標(biāo)碼段,塊布局單元,它把該段展平為指令的線性列表,以及優(yōu)化碼產(chǎn)生單元,它完成從原始指令到翻譯后代碼段指令的實際編譯。19.根據(jù)權(quán)利要求18的計算機結(jié)構(gòu)仿真系統(tǒng),這里塊檢出器創(chuàng)建一個描述要編譯的原始指令的控制流程圖,并把該控制流程圖傳遞給塊布局單元。20.在目的計算機結(jié)構(gòu)上仿真源計算機結(jié)構(gòu)的一個計算機結(jié)構(gòu)仿真系統(tǒng),包括多個解釋器,用于把源目標(biāo)碼單個地翻譯成相應(yīng)的翻譯后目標(biāo)碼,這里所述多個解釋器中的每一個在執(zhí)行翻譯后目標(biāo)碼指令的同時,實時地概要分析源目標(biāo)碼分支信息;以及編譯器,用于根據(jù)相應(yīng)的目標(biāo)碼中的分支指令,把來自所述多個解釋器中任何一個的源目標(biāo)碼指令組合成段,并在相應(yīng)的分支指令大于一閾值數(shù)時動態(tài)地編譯該源目標(biāo)碼段。21.根據(jù)權(quán)利要求20的計算機結(jié)構(gòu)仿真系統(tǒng),這里所述多個解釋器的每一個概要分析分支指令,并通過調(diào)用一分支記錄器來存儲尚未超過閾值數(shù)的分支指令。22.在目的計算機結(jié)構(gòu)上仿真源計算機結(jié)構(gòu)的一個計算機結(jié)構(gòu)仿真系統(tǒng),包括解釋器,用于把源目標(biāo)碼單個地翻譯成相應(yīng)的翻譯后目標(biāo)碼,這里解釋器通過存儲每個分支指令的執(zhí)行次數(shù)并把這執(zhí)行次數(shù)與一閾值數(shù)加以比較來概要分析源目標(biāo)碼的分支指令,這樣,超過閾值數(shù)的分支指令便指定為種子;以及編譯器,用于根據(jù)種子把源目標(biāo)碼指令組成段,并在所述解釋器翻譯和概要分析過程中動態(tài)編譯源目標(biāo)碼段。23.根據(jù)權(quán)利要求22的計算機結(jié)構(gòu)仿真系統(tǒng),這里每個段包含根據(jù)相應(yīng)的種子優(yōu)化源目標(biāo)碼所產(chǎn)生的指令,以及每個段作為一個單元被安裝和去掉安裝。24.根據(jù)權(quán)利要求23的計算機結(jié)構(gòu)仿真系統(tǒng),這里與尚未超過閾值數(shù)的分支指令相對應(yīng)的那些段不被編譯,而與不被編譯的那些段相對應(yīng)的分支指令被存儲在存儲器中。25.根據(jù)權(quán)利要求23的計算機結(jié)構(gòu)仿真系統(tǒng),進一步包括分支記錄器,用于存儲由所述解釋器確定的分支指令的分支概要分析信息,這里分支概要分析信息包括分支地址、分支后繼者、不分支后繼者、分支執(zhí)行計數(shù)和分支被采取計數(shù),分支概要分析信息是在分支指令仿真過程中由所述解釋器記錄的。26.根據(jù)權(quán)利要求23的計算機結(jié)構(gòu)仿真系統(tǒng),其中在執(zhí)行跳入或跳出可翻譯指令的分支指令之后放置代碼標(biāo)志的;以及通過參考相應(yīng)代碼標(biāo)志來檢驗對應(yīng)于分支指令的后繼指令是否可被翻譯。27.根據(jù)權(quán)利要求23的計算機結(jié)構(gòu)仿真系統(tǒng),其中在分支指令的一后繼指令執(zhí)行次數(shù)超過一閾值時啟動翻譯該分支指令。28.根據(jù)權(quán)利要求23的計算機結(jié)構(gòu)仿真系統(tǒng),其中當(dāng)存儲要被翻譯段的隊列達到一預(yù)先確定的容量時,通過提高閾值數(shù)來控制要被編譯段的編譯速率。29.根據(jù)權(quán)利要求23的計算機結(jié)構(gòu)仿真系統(tǒng),這里如果一個指令執(zhí)行過程沒能以預(yù)先確定的速率及時地執(zhí)行,則所述編譯器利用概要記錄追蹤其執(zhí)行情況,檢驗分支計數(shù)是否在一預(yù)先確定數(shù)之下,并如頁面錯那樣產(chǎn)生記錄分支信息的一個目標(biāo)。30.根據(jù)權(quán)利要求23的計算機結(jié)構(gòu)仿真系統(tǒng),進一步包括分支記錄裝置,用于存儲源目標(biāo)碼中分支指令的概要分析信息,其中包括執(zhí)行次數(shù),這里所述分支記錄裝置包括一個存儲被頻繁執(zhí)行的分支指令的概要分析信息的高速緩存和一個存儲不被頻繁執(zhí)行的分支指令的概要分析信息的分支日志。這里通過使分支地址信息和分支目的地信息組合,來把概要分析信息組織在高速緩存中,而概要分析信息是按進入各組的入口條目遞減順序存儲在多個組中的。31.根據(jù)權(quán)利要求23的計算機結(jié)構(gòu)仿真系統(tǒng),這里所述編譯器進一步包括塊檢出器,它根據(jù)該種子和該分支的概要分析信息來選擇要編譯的源目標(biāo)碼段,這里塊檢出器創(chuàng)建一個控制流程圖描述要編譯的源始指令;塊布局單元,它把控制流程圖展平為指令的線性列表;以及優(yōu)化代碼產(chǎn)生單元,它完成從原始指令到翻譯后代碼段指令的實際編譯。32.在多任務(wù)目的計算機結(jié)構(gòu)上仿真源計算機結(jié)構(gòu)的一個多任務(wù)計算機結(jié)構(gòu)的仿真系統(tǒng),包括解釋器任務(wù),把源目標(biāo)碼單個地翻譯成相應(yīng)的翻譯后目標(biāo)碼并確定在源目標(biāo)碼中分支指令的執(zhí)行次數(shù);以及編譯器任務(wù),和所解釋器任務(wù)在多任務(wù)目的計算機結(jié)構(gòu)上運行用于用于當(dāng)一相應(yīng)的分支指令執(zhí)行次數(shù)超過一閾值數(shù)時把源目標(biāo)碼指令組合成段并動態(tài)地編譯該段。33.根據(jù)權(quán)利要求32的多任務(wù)計算機結(jié)構(gòu)仿真系統(tǒng),這里所述多任務(wù)計算機結(jié)構(gòu)仿真系統(tǒng)是一個動態(tài)翻譯系統(tǒng),所述多任務(wù)計算機結(jié)構(gòu)系統(tǒng)進一步包括軟件反饋,用于使所述解釋器任務(wù)發(fā)送編譯請求的速率與所述編譯器任務(wù)完成編譯的速率相等,通過改變閾值數(shù)來不允許編譯器任務(wù)變?yōu)榭臻e。34.根據(jù)權(quán)利要求33的多任務(wù)計算機結(jié)構(gòu)仿真系統(tǒng),進一步包括存儲要被所述編譯器任務(wù)編譯的段的隊列,這里將閾值數(shù)與一最小閾值數(shù)進行比較,以啟動或關(guān)閉所述編譯器任務(wù)。全文摘要一種優(yōu)化目標(biāo)碼翻譯系統(tǒng)和方法,完成編譯和翻譯源操作系統(tǒng)上的標(biāo)的目標(biāo)碼,并同時完成優(yōu)化。對目標(biāo)碼的編譯和優(yōu)化是實時動態(tài)執(zhí)行的。編譯器完成分析和優(yōu)化,與基于模板的翻譯和解釋相比使仿真得到改善,從而使處理較大量級指令(如32位指令)的處理器可以仿真處理較小量指令(如16位或8位指令)的目標(biāo)處理器。優(yōu)化目標(biāo)碼翻譯器不需要在運行時之前知道靜態(tài)程序流程圖或目標(biāo)指令存儲器位置。文檔編號G06F9/45GK1270348SQ9912088公開日2000年10月18日申請日期1999年10月8日優(yōu)先權(quán)日1998年10月21日發(fā)明者理查德·A·勒辛,約瑟夫·A·班克,查爾斯·D·噶勒特,和田美加代,櫻井三男申請人:富士通株式會社